Objective

This project aims to develop a bankruptcy prediction model using Support Vector Machines (SVM) and Regularized Regression techniques in R. By leveraging historical financial data, the model will classify companies based on their likelihood of bankruptcy. The objective is to compare the performance of SVM and regularized regression methods in terms of predictive accuracy, interpretability, and robustness. Additionally, the project seeks to identify key financial indicators that contribute to bankruptcy risk, providing insights for financial analysts and decision-makers.

Introduction

Description of data:

  1. Bankrupt (int, target variable) – Indicates whether a company has gone bankrupt (0 = No, 1 = Yes).
  2. Current_Ratio (float) – Measures a company’s ability to pay short-term obligations (current assets / current liabilities).
  3. Quick_Ratio (float) – A liquidity metric that excludes inventory from current assets (quick assets / current liabilities).
  4. Total_debt_by_Total_networth (float) – Represents the company’s total debt in proportion to its net worth.
  5. Debt_ratio_Percent (float) – The percentage of a company’s assets financed by debt (total debt / total assets * 100).
  6. Operating_profit_by_Paidin_capital (float) – Measures profitability in relation to paid-in capital.
  7. Total_Asset_Turnover (float) – Indicates how efficiently a company utilizes its assets to generate sales.
  8. Working_Capital_to_Total_Assets (float) – Proportion of working capital in total assets, reflecting liquidity.
  9. Cash_Flow_to_Total_Assets (float) – Measures how much cash flow a company generates relative to its total assets.
  10. Cash_Flow_to_Liability (float) – Represents the proportion of cash flow available to cover liabilities.
  11. Current_Liability_to_Current_Assets (float) – Ratio of current liabilities to current assets, indicating liquidity risk.
  12. Gross_Profit_to_Sales (float) – Percentage of gross profit generated from total sales (gross profit / sales).
  13. Leverage_SD (categorical) – Categorized as “Low,” “Moderate,” or other levels, representing the company’s leverage risk.

Relationship between Features

Correlation Plot

The correlation matrix heatmap visually represents the relationships between financial variables and bankruptcy status. Notable insights include a negative correlation between Current Ratio and Bankruptcy (-0.32), indicating that companies with lower liquidity are more likely to go bankrupt. Total debt to Total net worth (0.38) and Debt Ratio Percent (0.22) show positive correlations with bankruptcy, suggesting higher leverage increases bankruptcy risk. A strong positive correlation (0.87) exists between Cash Flow to Liability and Cash Flow to Total Assets, while Current Ratio and Quick Ratio are also highly correlated (0.76), reflecting interdependencies among liquidity metrics. These insights can aid in financial risk assessment and bankruptcy prediction modeling.

df_num <- df
df_num$Bankrupt <- as.numeric(as.factor(df_num$Bankrupt))
numeric_features <- df_num %>% select_if(is.numeric)
# Compute correlation matrix
cor_matrix <- cor(numeric_features, use = "complete.obs")

# Plot heatmap
ggcorrplot(cor_matrix, method = "circle", type = "lower", lab = TRUE)

Relationship between Features

Relationship between highly correlated numerical features using Scatter Plot

The scatter plot of Cash Flow to Total Assets (CF/TA) vs. Cash Flow to Liabilities (CF/L) reveals a strong positive correlation, indicating that firms generating higher cash flow relative to their assets also tend to have higher cash flow relative to their liabilities. The majority of data points cluster along a linear trend, suggesting a consistent relationship between asset utilization and liability coverage. A few outliers above the main cluster suggest firms with exceptionally strong cash flow management or low liabilities, while those below the trend may indicate companies struggling with debt repayment. This analysis highlights the overall financial efficiency and potential risk factors within the dataset.

# Scatter plot using ggplot2
ggplot(df, aes(x=Cash_Flow_to_Total_Assets, y=Cash_Flow_to_Liability)) +
  geom_point(color="blue", alpha=0.5) +
  ggtitle(" Cash Flow to Total Assets vs Cash Flow to Liability") +
  xlab("Cash Flow to Total Assets ") +
  ylab("Cash Flow to Liability")

Relationship between Target Variable(Bankrupt) and a categorical feature using heatmap

The heatmap illustrates the relationship between leverage level (Total Debt to Total Net Worth) and bankruptcy status. The majority of firms classified under the “Moderate” leverage category are non-bankrupt (deep blue), while a smaller portion within this category has gone bankrupt (lighter purple). There are no firms categorized under “High” leverage, which might suggest data imbalance or absence of high-leverage firms in the dataset. This visualization highlights that companies with moderate leverage are more prevalent, but some still face bankruptcy risks.

df$Bankrupt <- as.factor(df$Bankrupt)

heatmap_data <- df %>%
  count(Bankrupt, Leverage_SD)

# Heatmap: Relationship between Default and Employment Status
ggplot(heatmap_data, aes(x = Bankrupt, y = Leverage_SD , fill = n)) +
  geom_tile() +
  labs(title = "Heatmap: Bankrupt vs Leverage Level (Total Debt to Total Net Worth)",
       x = "Bankrupt", y = "Leverage_SD") +
  scale_fill_gradient(low = "white", high = "blue") +
  theme_minimal()

Box Plot

The box plot compares the Current Ratio between bankrupt and non-bankrupt firms. Non-bankrupt firms (teal) tend to have a higher median current ratio compared to bankrupt firms (yellow), indicating better liquidity. The interquartile range (IQR) for non-bankrupt firms is wider, suggesting more variation in liquidity levels. Bankrupt firms have a lower median current ratio, with a more compressed distribution, showing that lower liquidity is associated with financial distress. Additionally, both groups have outliers, but the non-bankrupt firms exhibit a greater range of higher current ratios.

# Box Plot: Relationship between Bankrupt (Categorical) and  Current Ratio (Numerical)
ggplot(df, aes(x = Bankrupt, y = Current_Ratio, fill = Bankrupt)) +
  geom_boxplot() +
  labs(title = "Box Plot: Bankrupt vs Current Ratio",
       x = "Bankrupt Status", y = "Current Ratio") +
  theme_minimal() +
  scale_fill_brewer(palette = "Set3")

Skewness

The skewness analysis reveals that several financial variables exhibit significant skewness, indicating asymmetric distributions. Highly skewed variables include Quick Ratio, Total Debt to Total Net Worth, Debt Ratio Percent, Operating Profit by Paid-in Capital, Total Asset Turnover, Working Capital to Total Assets, Cash Flow to Total Assets, Cash Flow to Liability, Current Liability to Current Assets, and Gross Profit to Sales. Notably, Cash Flow to Liability (-9.58) and Total Debt to Total Net Worth (16.23) have extreme skewness values, suggesting a strong deviation from normality. These findings indicate potential data transformations may be necessary for statistical modeling.

# Select only numeric columns
numeric_vars <- df[, sapply(df, is.numeric)]
# Compute skewness for each numeric variable
skewness_values <- sapply(numeric_vars, skewness, na.rm = TRUE)

# Identify highly skewed variables
skewed_vars <- names(skewness_values[abs(skewness_values) > 1])

# Print results
#print("Skewness values:")
#print(skewness_values)
print("Highly skewed variables:")
[1] "Highly skewed variables:"
print(skewed_vars)
 [1] "Quick_Ratio"                         "Total_debt_by_Total_networth"       
 [3] "Debt_ratio_Percent"                  "Operating_profit_by_Paidin_capital" 
 [5] "Total_Asset_Turnover"                "Working_Capital_to_Total_Assets"    
 [7] "Cash_Flow_to_Total_Assets"           "Cash_Flow_to_Liability"             
 [9] "Current_Liability_to_Current_Assets" "Gross_Profit_to_Sales"              
# Create a histogram for Skewed Numeric Variable - No of Credit Accounts
#hist(numeric_vars$No_of_credit_acc, main = "Histogram of No of Credit Accounts", xlab = "No of Credit Accounts",      col = "lightblue",      breaks = 30)

Feature Transformation, Standardization & Normalization

Feature transformation and standardization are essential for regularized regression techniques like Ridge, Lasso, and Elastic Net to ensure optimal model performance. Many financial ratios exhibit high skewness, which can distort relationships and reduce predictive accuracy. Applying transformations (such as log or Box-Cox) helps normalize these distributions, improving model stability. Additionally, standardization ensures that all features contribute equally to the regularization penalty, preventing variables with larger scales from dominating the model. This preprocessing step enhances interpretability, mitigates multicollinearity, and improves convergence in optimization algorithms, leading to more reliable and generalizable predictions.

df_norm_stan <- df
df_norm_stan$Bankrupt <- as.numeric(as.factor(df$Bankrupt))
df_norm_stan$Leverage_SD <- as.numeric(as.factor(df$Leverage_SD))

# Normalization
normalize <- function(x) {
  return((x - min(x)) / (max(x) - min(x)))
}
df_norm_stan$Current_Ratio_norm <- normalize(df$Current_Ratio)
df_norm_stan$Quick_Ratio_norm <- normalize(df$Quick_Ratio)
df_norm_stan$Total_debt_by_Total_networth_norm <- normalize(df$Total_debt_by_Total_networth)
df_norm_stan$Operating_profit_by_Paidin_capital_norm <- normalize(df$Operating_profit_by_Paidin_capital)
df_norm_stan$Total_Asset_Turnover_norm <- normalize(df$Total_Asset_Turnover)
df_norm_stan$Cash_Flow_to_Total_Assets_norm <- normalize(df$Cash_Flow_to_Total_Assets)
df_norm_stan$Cash_Flow_to_Liability_norm <- normalize(df$Cash_Flow_to_Liability)
df_norm_stan$Current_Liability_to_Current_Assets_norm <- normalize(df$Current_Liability_to_Current_Assets)
df_norm_stan$Gross_Profit_to_Sales_norm <- normalize(df$Gross_Profit_to_Sales)
df_norm_stan$Debt_ratio_Percent_norm <- normalize(df$Debt_ratio_Percent)
df_norm_stan$Working_Capital_to_Total_Assets <- normalize(df$Working_Capital_to_Total_Assets)


# Log transformation


df_norm_stan$Quick_Ratio_trans <- log(df$Quick_Ratio + 1)
df_norm_stan$Total_debt_by_Total_networth_trans <- log(df$Total_debt_by_Total_networth + 1)
df_norm_stan$Operating_profit_by_Paidin_capital_trans <- log(df$Operating_profit_by_Paidin_capital+1)
df_norm_stan$Total_Asset_Turnover_trans <- log(df$Total_Asset_Turnover+1)
df_norm_stan$Cash_Flow_to_Total_Assets_trans <- log(df$Cash_Flow_to_Total_Assets+1)
df_norm_stan$Cash_Flow_to_Liability_trans <- log(df$Cash_Flow_to_Liability+1)
df_norm_stan$Current_Liability_to_Current_Assets_trans <- log(df$Current_Liability_to_Current_Assets+1)
df_norm_stan$Gross_Profit_to_Sales_trans <- log(df$Gross_Profit_to_Sales+1)
df_norm_stan$Debt_ratio_Percent_trans <- log(df$Debt_ratio_Percent+1)
df_norm_stan$Working_Capital_to_Total_Assets_trans <- log(df$Working_Capital_to_Total_Assets+1)



# Standardization
standardize <- function(x) {
  return((x - mean(x)) / sd(x))
}

df_norm_stan$Current_Ratio_stand <- standardize(df_norm_stan$Current_Ratio)
df_norm_stan$Quick_Ratio_stand <- standardize(df_norm_stan$Quick_Ratio_trans)
df_norm_stan$Total_debt_by_Total_networth_stand <- standardize(df_norm_stan$Total_debt_by_Total_networth_trans)
df_norm_stan$Operating_profit_by_Paidin_capital_stand <- standardize(df_norm_stan$Operating_profit_by_Paidin_capital_trans)
df_norm_stan$Total_Asset_Turnover_stand <- standardize(df_norm_stan$Total_Asset_Turnover_trans)
df_norm_stan$Cash_Flow_to_Total_Assets_stand <- standardize(df_norm_stan$Cash_Flow_to_Total_Assets_trans)
df_norm_stan$Cash_Flow_to_Liability_stand <- standardize(df_norm_stan$Cash_Flow_to_Liability_trans)
df_norm_stan$Current_Liability_to_Current_Assets_stand <- standardize(df_norm_stan$Current_Liability_to_Current_Assets_trans)
df_norm_stan$Gross_Profit_to_Sales_stand <- standardize(df_norm_stan$Gross_Profit_to_Sales_trans)
df_norm_stan$Debt_ratio_Percent_stand <- standardize(df_norm_stan$Debt_ratio_Percent_trans)
df_norm_stan$Working_Capital_to_Total_Assets_stand <- standardize(df_norm_stan$Working_Capital_to_Total_Assets_trans)


#full_model <- glm(Default ~ ., data = new_df, family = binomial)  
#summary(full_model)

#final_model <- glm(Default ~ Checking_amount+Term+Credit_score+Age, data = new_df, family = binomial )
#summary(final_model)

# Keep only standardized columns and target variable
df_stan <- df_norm_stan %>% select(Bankrupt, Leverage_SD, ends_with("_stand"))

# View structure of the standardized dataset
#str(df_stan)

Principal Component Analysis

The Principal Component Analysis (PCA) results indicate that the first few principal components capture most of the variance in the dataset, with PC1 alone explaining 27.7% and the first five components accounting for approximately 70.7% of the total variance. The scree plot further confirms that beyond a certain number of components, the variance explained drops significantly, suggesting that dimensionality reduction is feasible. The scatter plot of the first two principal components shows some clustering but also highlights outliers that may influence the analysis. Overall, applying PCA in this project is beneficial as it helps reduce dimensionality while retaining most of the important information, improving model efficiency and interpretability, especially when dealing with highly correlated and skewed variables.

# Step 1: Convert categorical variables to numeric using one-hot encoding
df_numeric <- df %>%
 mutate(across(c(Bankrupt, Leverage_SD), as.factor)) %>%  # Convert to factors
   dummyVars(~ ., data = .) %>%      
  predict(newdata = df) %>% 
   as.data.frame()
 
 # Step 2: Select only numeric features
 numeric_data <- df_numeric %>% select_if(is.numeric)
 
 # Step 3: Handle missing and infinite values
 numeric_data[is.na(numeric_data)] <- 0   # Replace NAs with 0 (or use median imputation)
 
 constant_cols <- apply(numeric_data, 2, function(col) var(col, na.rm = TRUE) == 0)
numeric_data <- numeric_data[, !constant_cols]

 
 # Step 4: Standardize the data (only after handling missing values)
 scaled_data <- scale(numeric_data)
 
 # Step 5: Perform PCA
 pca_result <- prcomp(scaled_data, center = TRUE, scale = TRUE)
 
 summary(pca_result)
Importance of components:
                          PC1    PC2    PC3    PC4     PC5     PC6     PC7
Standard deviation     2.0387 1.4023 1.3164 1.2205 1.12261 1.03752 0.97403
Proportion of Variance 0.2771 0.1311 0.1155 0.0993 0.08402 0.07176 0.06325
Cumulative Proportion  0.2771 0.4082 0.5237 0.6230 0.70702 0.77879 0.84204
                          PC8     PC9    PC10    PC11   PC12    PC13      PC14
Standard deviation     0.8068 0.72611 0.66834 0.63905 0.4549 0.35950 1.357e-14
Proportion of Variance 0.0434 0.03515 0.02978 0.02723 0.0138 0.00862 0.000e+00
Cumulative Proportion  0.8854 0.92058 0.95036 0.97759 0.9914 1.00000 1.000e+00
                            PC15
Standard deviation     4.693e-17
Proportion of Variance 0.000e+00
Cumulative Proportion  1.000e+00
 # Step 6: Scree Plot (to decide number of components to keep)
 screeplot(pca_result, type = "lines", main = "Scree Plot")

 # Step 7: Biplot (PCA visualization)
# biplot(pca_result, scale = 0)
 
 # Step 8: Scatter plot of first two principal components
 pca_df <- as.data.frame(pca_result$x)
 ggplot(pca_df, aes(x = PC1, y = PC2)) +
   geom_point(size = 3, alpha = 0.7, color = "blue") +
 labs(title = "PCA - First Two Principal Components") +
   theme_minimal()

Regularization

  • Regularized Logistic Regression:

The results from the LASSO regularized regression analysis provide key insights into feature selection and model performance.

Coefficient Path Plot (Left Panel): This plot shows how the regression coefficients shrink as the regularization parameter (λ) increases. Initially, at low λ values (right side of the plot), most predictors have nonzero coefficients, meaning they are actively contributing to the model. As λ increases (moving left), many coefficients shrink toward zero, demonstrating LASSO’s ability to enforce sparsity by selecting only the most important features. The red dashed line represents the λ value that minimizes cross-validation error, striking a balance between model complexity and predictive power. The blue dashed line represents the most regularized model within one standard error of the minimum cross-validation error, often chosen for better generalization.

Model Fit Plot (Right Panel): This plot illustrates the binomial deviance (a measure of model error) for different λ values. The curve initially decreases, indicating that moderate regularization improves model performance by reducing overfitting. The red points represent mean deviance values, with error bars showing variability. The deviance reaches its lowest point around λ≈−5 (log scale), after which it increases as excessive regularization removes too many important features, leading to underfitting.

df <- df_stan

# Split the data into predictors (X) and response (y)
X <- model.matrix(Bankrupt ~ ., df_stan)[,-1]  # Remove the intercept column
y <- df$Bankrupt

# Split the data into training and testing sets
set.seed(123)
trainIndex <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- X[trainIndex, ]
X_test <- X[-trainIndex, ]
y_train <- y[trainIndex]
y_test <- y[-trainIndex]

#table(y_test)
####################
# Fit LASSO model
####################
lasso_model <- glmnet(X_train, y_train, family = "binomial", alpha = 1)

# Cross-validation to find the optimal lambda
cv_lasso <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 1)

# Optimal lambda
lambda_lasso <- cv_lasso$lambda.min

# Refit the model with the optimal lambda
lasso_model_opt <- glmnet(X_train, y_train, 
                          family = "binomial", 
                          alpha = 1, 
                          lambda = lambda_lasso)
## Visualize the impact of lambda on shrinking coefficients
# Plot coefficient paths
par(mar=c(5,4,6,2), mfrow=c(1,2)) # 
plot(lasso_model, xvar = "lambda", label = TRUE,
     col = rainbow(8),
     lwd = 1,
     main = "Coefficient Path Plot: LASSO",
     cex.main = 0.8)
text(-6, 0.4, "minimum CV error", col="red", cex = 0.6 )
abline(v = log(cv_lasso$lambda.min), col = "red", lty = 4, lwd = 1)
abline(v = log(cv_lasso$lambda.1se), col = "blue", lty = 4, lwd = 1)

plot(cv_lasso, main="Measure of Model Fit: LASSO", cex.main = 0.8)

####################
# Fit Ridge model
####################
ridge_model <- glmnet(X_train, y_train, family = "binomial", alpha = 0)

# Cross-validation to find the optimal lambda
cv_ridge <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 0)

# Optimal lambda
lambda_ridge <- cv_ridge$lambda.min

# Refit the model with the optimal lambda
ridge_model_opt <- glmnet(X_train, y_train, 
                          family = "binomial", 
                          alpha = 0, 
                          lambda = lambda_ridge)
############################################
# Fit Elastic Net model (e.g., alpha = 0.5)
############################################
elastic_model <- glmnet(X_train, y_train, family = "binomial", alpha = 0.5)

# Cross-validation to find the optimal lambda
cv_elastic <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 0.5)

# Optimal lambda
lambda_elastic <- cv_elastic$lambda.min

# Refit the model with the optimal lambda
elastic_model_opt <- glmnet(X_train, y_train, 
                            family = "binomial", 
                            alpha = 0.5, 
                            lambda = lambda_elastic)
lasso.coef <- as.matrix(coef(lasso_model_opt))
ridge.coef <- as.matrix(coef(ridge_model_opt))
elastic.coef <- as.matrix(coef(elastic_model_opt))
regularized.coef <- data.frame(lasso = lasso.coef[,1],
                               ridge = ridge.coef[,1],
                          elasticnet = elastic.coef[,1])
pander(regularized.coef)
  lasso ridge elasticnet
(Intercept) -1.591 -0.3881 -1.548
Leverage_SD 0 -0.5822 0
Current_Ratio_stand -0.3381 -0.4755 -0.3579
Quick_Ratio_stand 0 -0.02971 0
Total_debt_by_Total_networth_stand 0 0.286 0.00328
Operating_profit_by_Paidin_capital_stand -0.8337 -0.6194 -0.6786
Total_Asset_Turnover_stand -0.1303 -0.1888 -0.1204
Cash_Flow_to_Total_Assets_stand -0.169 -0.2153 -0.182
Cash_Flow_to_Liability_stand -0.09913 -0.1134 -0.08148
Current_Liability_to_Current_Assets_stand 0 -0.05365 0
Gross_Profit_to_Sales_stand -0.1333 -0.2754 -0.1738
Debt_ratio_Percent_stand 0.9952 0.7918 0.8882
Working_Capital_to_Total_Assets_stand 0 0.07734 0

Optimal Cuttoff Probability Determination

Optimal Cutoff Probability for this model is 0 for the reasons below:

Class Imbalance: A highly imbalanced dataset may cause the model to favor the majority class, leading to an optimal cut-off of 0 for maximum accuracy.

Over-Regularization: Strong regularization (high lambda values) can shrink coefficients, reducing the model’s ability to differentiate between classes.

Bias in Probability Estimates: If predicted probabilities are skewed toward higher values, the model may classify most instances as the positive class, making 0 the best cut-off.

Accuracy as a Misleading Metric: Since accuracy does not account for class distribution, alternative metrics such as AUC-ROC, precision-recall, and F1-score should be considered for better threshold selection.

############################
# Predict on the test set: type = "class" uses the default 
# cut-off probability to be 0.5.
predict_lasso <- predict(lasso_model_opt, newx = X_test, type = "response")
predict_ridge <- predict(ridge_model_opt, newx = X_test, type = "response")
predict_elastic <- predict(elastic_model_opt, newx = X_test, type = "response")


###########################################
## Optimal cutoff probability determination
seq.cut <- seq(0,1, length=50)
# y is a vector of 0 and 1
acc.lasso <- NULL
acc.ridge <- NULL
acc.elastic <- NULL
for (i in 1:length(seq.cut)){
   predy.lasso <- ifelse(predict_lasso >seq.cut[i], 1, 0)
   predy.ridge<- ifelse(predict_ridge >seq.cut[i], 1, 0)
   predy.elastic<- ifelse(predict_elastic >seq.cut[i], 1, 0)
   ##
   acc.lasso[i] <- mean(y_test  == predy.lasso)
   acc.ridge[i] <- mean(y_test == predy.ridge)
   acc.elastic[i] <- mean(y_test  == predy.elastic)
}
## optimal cut-off: if the maximum accuracy occurs at multiple
## cut-off probabilities, the average of these cutoff probabilities
## will be defined as the optimal cutoff probability
opt.cut.lasso <- mean(seq.cut[which(acc.lasso==max(acc.lasso))])
opt.cut.ridge<- mean(seq.cut[which(acc.ridge==max(acc.ridge))])
opt.cut.elastic <- mean(seq.cut[which(acc.elastic==max(acc.elastic))])
##



# Print optimal cutoff probabilities
#cat("Optimal Cutoff for LASSO:", opt.cut.lasso, "\n")
#cat("Optimal Cutoff for Ridge:", opt.cut.ridge, "\n")
#cat("Optimal Cutoff for Elastic Net:", opt.cut.elastic, "\n")

acc.data <- data.frame(prob = rep(seq.cut,3), 
                       acc=c(acc.lasso, acc.ridge, acc.elastic), 
                       group = c(rep("lasso",50), rep("ridge",50), rep("elastic",50)))

Accuracy Plot

The accuracy vs. cut-off probability plot indicates that all three models—LASSO, Ridge, and Elastic Net—exhibit nearly identical classification performance, achieving maximum accuracy (~0.7511) at a low cut-off threshold. As the cut-off increases, accuracy declines sharply, suggesting a strong class imbalance or threshold sensitivity. The models predominantly favor the majority class at lower thresholds, leading to higher accuracy but potentially poor sensitivity for minority class detection. The rapid decline in accuracy beyond a 0.1 cut-off suggests that misclassification rates increase significantly. This indicates that accuracy alone may not be the best evaluation metric, warranting additional assessments such as AUC-ROC and F1-score. Optimizing the threshold using these alternative metrics could enhance predictive performance.

##
gg.acc <- ggplot(data = acc.data, aes(x=prob, y = acc, color = group)) +
  geom_line() +
  annotate("text", x = 0.6, y = 0.45, 
           label = paste( "Accuracy: ", round(max(acc.lasso),5), 
                         "\nAccuracy: ", round(max(acc.ridge),5), 
                         "\nAccuracy: ", round(max(acc.elastic),5)), 
           size = 3, 
           color = "navy") +
  ggtitle("Cut-off Probability vs Accuracy") +
  labs(x = "cut-off Probability", 
       y = "accuracy", color = "Group") +
  theme(plot.title = element_text(hjust = 0.5))

##
ggplotly(gg.acc)

Confusion Matrix

The performance metrics for LASSO, Ridge, and Elastic Net regression models indicate strong recall (0.9704) across all models, suggesting that they effectively identify the positive class. However, specificity is relatively low (0.3929–0.375), meaning the models struggle to correctly classify negative instances, likely due to class imbalance. Precision values (~0.82) and F1-scores (~0.89) confirm a good balance between precision and recall, favoring sensitivity. The balanced accuracy values (0.6816 for LASSO, 0.6638 for Ridge, and 0.6727 for Elastic Net) indicate moderate overall performance, with LASSO slightly outperforming the other models. These results suggest that while the models are strong in detecting positives, further threshold tuning or alternative evaluation metrics (such as AUC-ROC) may improve classification of negative cases.

#######################################
## using the optimal cutoff probability to predict labels
## 
#pred.lab.lasso <- ifelse(predict_lasso >opt.cut.lasso, 1, 0)
#pred.lab.ridge<- ifelse(predict_ridge >opt.cut.ridge, 1, 0)
#pred.lab.elastic<- ifelse(predict_elastic >opt.cut.elastic, 1, 0)


new_threshold <- 0.5  # Try adjusting this
pred.lab.lasso <- ifelse(predict_lasso > new_threshold, 1, 0)
pred.lab.ridge<- ifelse(predict_ridge >new_threshold, 1, 0)
pred.lab.elastic<- ifelse(predict_elastic >new_threshold, 1, 0)


#################################
# Convert predictions to factors
pred.lab.lasso.fct <- as.factor(pred.lab.lasso)
pred.lab.ridge.fct <- as.factor(pred.lab.ridge)
pred.lab.elastic.fct <- as.factor(pred.lab.elastic)
y_test <- factor(ifelse(y_test == 2, 1, 0), levels = c(0, 1))

#table(pred.lab.lasso.fct)
#table(y_test)

# Confusion Matrix and Metrics
confusion.lasso <- confusionMatrix(pred.lab.lasso.fct, y_test)
confusion.ridge<- confusionMatrix(pred.lab.ridge.fct, y_test)
confusion.elastic <- confusionMatrix(pred.lab.elastic.fct, y_test)

## Commonly used performance measured
PerfMeasures <- cbind(lasso = confusion.lasso$byClass, 
                     ridge = confusion.ridge$byClass, 
                     elastic = confusion.elastic$byClass)
pander(PerfMeasures)
  lasso ridge elastic
Sensitivity 0.9704 0.9704 0.9704
Specificity 0.3929 0.3571 0.375
Pos Pred Value 0.8283 0.82 0.8241
Neg Pred Value 0.8148 0.8 0.8077
Precision 0.8283 0.82 0.8241
Recall 0.9704 0.9704 0.9704
F1 0.8937 0.8889 0.8913
Prevalence 0.7511 0.7511 0.7511
Detection Rate 0.7289 0.7289 0.7289
Detection Prevalence 0.88 0.8889 0.8844
Balanced Accuracy 0.6816 0.6638 0.6727

ROC Analysis

The ROC curve compares the performance of three logistic regression models—LASSO, Ridge, and Elastic Net—based on their ability to distinguish between classes. The area under the curve (AUC) values indicate that all three models perform similarly, with Elastic Net achieving the highest AUC (0.841), followed closely by LASSO (0.84) and Ridge (0.838). These values suggest that the models have good predictive capabilities, with Elastic Net slightly outperforming the others in terms of classification accuracy.

# library(pROC)
# Predicted probabilities for each model: type = "response"
prob_lasso <- predict(lasso_model_opt, newx = X_test, type = "response")
prob_ridge <- predict(ridge_model_opt, newx = X_test, type = "response")
prob_elastic <- predict(elastic_model_opt, newx = X_test, type = "response")

# Compute ROC curves: roc object contains a lot information including
# sensitivity, specificity, AUC, etc.
roc_lasso <- roc(y_test, prob_lasso)
roc_ridge <- roc(y_test, prob_ridge)
roc_elastic <- roc(y_test, prob_elastic)

# Compute AUC values
auc_lasso <- auc(roc_lasso)
auc_ridge <- auc(roc_ridge)
auc_elastic <- auc(roc_elastic)

## LASSO
sen.lasso <- roc_lasso$sensitivities
spe.lasso <- roc_lasso$specificities
auc.lasso <- roc_lasso$auc

## Ridge
sen.ridge <- roc_ridge$sensitivities
spe.ridge <- roc_ridge$specificities
auc.ridge <- roc_ridge$auc

## Elastic Net
sen.elastic <- roc_elastic$sensitivities
spe.elastic <- roc_elastic$specificities
auc.elastic <- roc_elastic$auc

## Plotting the ROC curves: three colors - green, orange, and purple

plot(1-spe.lasso, sen.lasso, 
     type = "l",
     col = "green", 
     xlim=c(0,1),
     xlab = "1 - specificity",
     ylab = "sensitivity",
     main = "ROC Curves for LASSO, Ridge, and Elastic Net")
lines(1-spe.ridge, sen.ridge, col = "orange")
lines(1-spe.elastic, sen.elastic, col = "purple")
abline(0,1, type = "l", lty = 2, col = "steelblue", lwd = 1)

# Add legend
legend("bottomright", legend = c(paste("LASSO (AUC =", round(auc_lasso, 3), ")"),
                                paste("Ridge (AUC =", round(auc_ridge, 3), ")"),
                                paste("Elastic Net (AUC =", round(auc_elastic, 3), ")")),
       col = c("green", "orange", "purple"), lty = 1, cex = 0.8, bty = "n")

Support Vector Machine for Classification

The results represent a confusion matrix from a Support Vector Machine (SVM) classification model, where:

  • 206 true negatives (correctly classified as class 0)
  • 14 false positives (misclassified as class 1 but actually class 0)
  • 58 false negatives (misclassified as class 0 but actually class 1)
  • 61 true positives (correctly classified as class 1)

Analysis:

  1. Accuracy: The model achieves an accuracy of approximately 78.76%.

  2. Precision (Positive Predictive Value) for Class 1: When the model predicts class 1, it is correct about 81.33% of the time.

  3. Recall (Sensitivity or True Positive Rate) for Class 1: The model correctly identifies 51.26% of actual class 1 instances, indicating that it misses a significant number of positives.

  4. Specificity (True Negative Rate): The model correctly identifies 93.63% of actual class 0 instances, showing strong performance in recognizing negatives.

  5. Optimal Cutoff: 0.1742

    • The classification threshold (cutoff) was optimized to 0.1742, meaning that any predicted probability above this is classified as class 1.
    • A low threshold like this suggests the model is designed to be more sensitive to detecting class 1, likely to reduce false negatives. However, this also increases the false positive rate.

The SVM classifier performs well in identifying class 0 (high specificity) but struggles with class 1 (low recall). The optimal threshold (0.1742) was chosen to balance sensitivity and specificity, but the recall for class 1 remains relatively low. If misclassifying class 1 is costly, adjusting the threshold or model tuning (e.g., changing kernel functions, regularization parameters) may improve recall.

# Load the dataset

df$Bankrupt <- as.factor(df$Bankrupt)

# Train-test split
set.seed(123)
index <- sample(1:nrow(df), 0.7 * nrow(df))
train.data <- df[index, ]
test.data <- df[-index, ]

# Set up custom cross-validation control
tune_control <- tune.control(cross = 5, nrepeat = 1)

# Perform a grid search for the best hyperparameters
tune.RBF <- tune(
  svm,
  Bankrupt ~ .,
  data = train.data,
  kernel = "radial",
  ranges = list(cost = 10^(-1:2), gamma = c(0.1, 0.5, 1, 2)),
  tunecontrol = tune_control
)

# Extract the best model
best.RBF <- tune.RBF$best.model

# Train final model with probability estimation
tuned.svm <- svm(
  Bankrupt ~ .,
  data = train.data,
  kernel = "radial",
  cost = best.RBF$cost,
  gamma = best.RBF$gamma,
  probability = TRUE
)

# Predict probabilities
pred.probs <- predict(tuned.svm, test.data, probability = TRUE)
pred.probs <- attr(pred.probs, "probabilities")[, 2]  # Extract probabilities for class "1"

# Compute optimal cutoff by minimizing distance to (0,1) in ROC space
pred <- prediction(pred.probs, test.data$Bankrupt)
perf <- performance(pred, "tpr", "fpr")

cutoffs <- data.frame(
  cutoff = perf@alpha.values[[1]],
  tpr = perf@y.values[[1]],
  fpr = perf@x.values[[1]]
)
cutoffs$distance <- sqrt((1 - cutoffs$tpr)^2 + cutoffs$fpr^2)
optimal.cutoff <- cutoffs$cutoff[which.min(cutoffs$distance)]

# Apply optimal cutoff
pred.optimal.class <- ifelse(pred.probs > optimal.cutoff, 1, 0)

# Confusion matrix with optimal cutoff
confusion.matrix.optimal <- table(Predicted = pred.optimal.class, Actual = test.data$Bankrupt)
print(confusion.matrix.optimal)
         Actual
Predicted   1   2
        0 206  14
        1  58  61
# Print optimal cutoff value
print(paste("Optimal Cutoff: ", optimal.cutoff))
[1] "Optimal Cutoff:  0.17423346704307"

Accuracy

# Calculate accuracy
accuracy <- sum(diag(confusion.matrix.optimal)) / sum(confusion.matrix.optimal)
cat("\n\n Accuracy:", accuracy, "\n")


 Accuracy: 0.7876106 

ROC

The ROC curve compares the classification performance of three models: Support Vector Machine (SVM) with a linear kernel, SVM with a radial kernel, and logistic regression. The Area Under the Curve (AUC) values indicate that the radial kernel SVM achieves the best performance (AUC = 0.8756), followed by the linear kernel SVM (AUC = 0.8493), and logistic regression (AUC = 0.8319). This suggests that the radial kernel SVM provides the highest discriminatory power in distinguishing between classes, making it the most effective model for this classification task.

##
## Set up custom cross-validation control
tune.control <- tune.control(
  cross = 5,  # Use 5-fold cross-validation, the default is 10-fold cross-validation
  nrepeat = 1 # Number of repetitions (for repeated cross-validation)
)
## 
## Perform a grid search for the best hyperparameters
tune.lin <- tune(
  svm,          # using the primary svm() algorithm to tune parameter
  Bankrupt ~ ., # model formula
  data = train.data,
  kernel = "linear",    # You can change the kernel if needed
  ranges = list(
    cost = 10^(-1:2)   # tune the hyperparameter C in the loss function
  ),
  tunecontrol = tune.control  # Use custom cross-validation settings
)
# Print the tuning results for inspection
# print(tune_result)
##
## Extract the best model and hyperparameters
best.lin <- tune.lin$best.model
best.cost.lin <- best.lin$cost

tune.RBF <- tune(
  svm, 
  Bankrupt ~ ., 
  data = train.data,
  kernel = "radial",
  ranges = list(
    cost = 10^(-1:2),  # Tune cost
    gamma = 10^(-3:1)  # Tune gamma
  ),
  tunecontrol = tune.control
)

# Extract best hyperparameters
best.cost.RBF <- tune.RBF$best.model$cost
best.gamma.RBF <- tune.RBF$best.model$gamma

# Print the best hyperparameters for inspection
# cat("Best Cost:", best_cost, "\n")
# cat("Best Gamma:", best_gamma, "\n")
##
## Train the final SVM model with the best hyperparameters
final.lin <- svm(
  Bankrupt ~ .,
  data = train.data,
  kernel = "linear",
  cost = best.cost.lin,
  probability = TRUE
)

## Request to return probabilities in final.RBF

final.RBF <- svm(
  Bankrupt ~ .,
  data = train.data,
  kernel = "radial",
  cost = best.cost.RBF,
  gamma = best.gamma.RBF,
  probability = TRUE
)
########################
###  logistic regression
logit.fit <- glm(Bankrupt ~ ., data = train.data, family = binomial)
AIC.logit <- step(logit.fit, direction = "both", trace = 0)
pred.logit <- predict(AIC.logit, test.data, type = "response")

###
####################
# ROC Curve and AUC
pred.prob.lin <- predict(final.lin, test.data, probability = TRUE)
pred.prob.RBF <- predict(final.RBF, test.data, probability = TRUE)
##
## extracting probabilities
prob.linear <- attr(pred.prob.lin, "probabilities")[, 2]
prob.radial <- attr(pred.prob.RBF, "probabilities")[, 2]
###
roc_lin <- roc(test.data$Bankrupt, prob.linear)
roc_RBF <- roc(test.data$Bankrupt, prob.radial)
roc_logit <- roc(test.data$Bankrupt, pred.logit)
### Sen-Spe
lin.sen <- roc_lin$sensitivities
lin.spe <- roc_lin$specificities
rad.sen <- roc_RBF$sensitivities
rad.spe <- roc_RBF$specificities
logit.sen <- roc_logit$sensitivities
logit.spe <- roc_logit$specificities
## AUC
auc.lin <- roc_lin$auc
auc.rad <- roc_RBF$auc
auc.logit <- roc_logit$auc
## Plotting ROC curves

plot(1-lin.spe, lin.sen,  
     xlab = "1 - specificity",
     ylab = "sensitivity",
     col = "darkred",
     type = "l",
     lty = 1,
     lwd = 1,
     main = "ROC Curves of SVM")
lines(1-rad.spe, rad.sen, 
      col = "blue",
      lty = 1,
      lwd = 1)
lines(1-logit.spe, logit.sen,      
      col = "orange",
      lty = 1,
      lwd = 1)
abline(0,1, col = "skyblue3", lty = 2, lwd = 2)
abline(v=c(0.049,0.151), lty = 3, col = "darkgreen")
legend("bottomright", c("Linear Kernel", "Radial Kernel", "Logistic Regression"),
       lty = c(1,1,1), lwd = rep(1,3),
       col = c("red", "blue", "orange"),
       bty="n",cex = 0.8)
## annotation - AUC
text(0.8, 0.46, paste("Linear AUC: ", round(auc.lin,4)), cex = 0.8)
text(0.8, 0.4, paste("Radial AUC: ", round(auc.rad,4)), cex = 0.8)
text(0.8, 0.34, paste("Logistic AUC: ", round(auc.logit,4)), cex = 0.8)

Model Comparison

Regularized Classification vs Support Vector Machine Classification:

Regularized classification (Lasso, Ridge, and Elastic Net) and Support Vector Machine (SVM) classification exhibit distinct strengths. Regularized classification models demonstrate high sensitivity (97.04%) across all methods, meaning they effectively capture positive cases. However, their specificity is relatively low (ranging from 35.71% to 39.29%), indicating they struggle with correctly identifying negatives. In contrast, the SVM classifier achieves much higher specificity (93.63%), meaning it is excellent at identifying negative cases, but its sensitivity is significantly lower (51.26%), leading to a higher rate of missed positive cases. Additionally, the SVM model has an overall accuracy of 78.76%, balancing precision and recall with an optimized cutoff of 0.1742. Ultimately, regularized classification models are better suited when capturing positives is critical, whereas SVM is preferable when correctly identifying negatives is a priority.

LS0tDQp0aXRsZTogIlVzaW5nIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzIGFuZCBSZWd1bGFyaXplZCBSZWdyZXNzaW9uIGZvciBCYW5rcnVwdGN5IFByZWRpY3Rpb24gaW4gUiINCmF1dGhvcjogIkJoYXZhbmEgS2FwcGFsYSINCmRhdGU6ICIyMDI1LTAzLTE4Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiAgDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCg0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDIycHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInNtb3RlZmFtaWx5IikpIHsNCmluc3RhbGwucGFja2FnZXMoInNtb3RlZmFtaWx5IikNCmxpYnJhcnkoc21vdGVmYW1pbHkpICAjIEZvciBTTU9URQ0KfQ0KDQppZiAoIXJlcXVpcmUoImh0bWx0b29scyIpKSB7DQojIFVwZGF0ZSB0aGUgaHRtbHRvb2xzIHBhY2thZ2UNCmluc3RhbGwucGFja2FnZXMoImh0bWx0b29scyIpDQpsaWJyYXJ5KGh0bWx0b29scykNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCiMgTG9hZCB0aGUgcGxvdGx5IHBhY2thZ2UNCmxpYnJhcnkocGxvdGx5KQ0KfQ0KDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoIlZJTSIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJWSU0iKSAgIyBGb3IgS05OIGltcHV0YXRpb24NCmxpYnJhcnkoVklNKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpICAjIEZvciBvbmUtaG90IGVuY29kaW5nDQogIGxpYnJhcnkoY2FyZXQpDQp9DQppZiAoIXJlcXVpcmUoIm1pY2UiKSkgew0KaW5zdGFsbC5wYWNrYWdlcygibWljZSIpICAgIyBGb3IgbXVsdGlwbGUgaW1wdXRhdGlvbiAob3B0aW9uYWwpDQpsaWJyYXJ5KG1pY2UpDQp9DQoNCmlmICghcmVxdWlyZSgicFJPQyIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJwUk9DIikgICAjIEZvciBST0MgQ3VydmUNCmxpYnJhcnkocFJPQykNCn0NCmlmICghcmVxdWlyZSgiY2FUb29scyIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJjYVRvb2xzIikgICAjIEZvciBTcGxpdHRpbmcgRGF0YQ0KbGlicmFyeShjYVRvb2xzKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdnY29ycnBsb3QiKSkgew0KaW5zdGFsbC5wYWNrYWdlcygiZ2djb3JycGxvdCIpICAjIEluc3RhbGwgaWYgbmVlZGVkDQpsaWJyYXJ5KGdnY29ycnBsb3QpDQp9DQoNCg0KaWYgKCFyZXF1aXJlKCJza2ltciIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJza2ltciIpDQpsaWJyYXJ5KHNraW1yKQ0KfQ0KDQppZiAoIXJlcXVpcmUoIkRhdGFFeHBsb3JlciIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJEYXRhRXhwbG9yZXIiKQ0KbGlicmFyeShEYXRhRXhwbG9yZXIpDQp9DQoNCmlmICghcmVxdWlyZSgiZ2xtbmV0IikpIHsNCmluc3RhbGwucGFja2FnZXMoImdsbW5ldCIpDQpsaWJyYXJ5KGdsbW5ldCkNCn0NCg0KDQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KbGlicmFyeShwYW5kZXIpDQp9DQoNCmlmICghcmVxdWlyZSgiUk9DUiIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJST0NSIikNCmxpYnJhcnkoUk9DUikNCn0NCg0KaWYgKCFyZXF1aXJlKCJlMTA3MSIpKSB7DQppbnN0YWxsLnBhY2thZ2VzKCJlMTA3MSIpDQpsaWJyYXJ5KGUxMDcxKQ0KfQ0KIyMgDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNCiMjIE9iamVjdGl2ZQ0KDQpUaGlzIHByb2plY3QgYWltcyB0byBkZXZlbG9wIGEgYmFua3J1cHRjeSBwcmVkaWN0aW9uIG1vZGVsIHVzaW5nIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzIChTVk0pIGFuZCBSZWd1bGFyaXplZCBSZWdyZXNzaW9uIHRlY2huaXF1ZXMgaW4gUi4gQnkgbGV2ZXJhZ2luZyBoaXN0b3JpY2FsIGZpbmFuY2lhbCBkYXRhLCB0aGUgbW9kZWwgd2lsbCBjbGFzc2lmeSBjb21wYW5pZXMgYmFzZWQgb24gdGhlaXIgbGlrZWxpaG9vZCBvZiBiYW5rcnVwdGN5LiBUaGUgb2JqZWN0aXZlIGlzIHRvIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIFNWTSBhbmQgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtZXRob2RzIGluIHRlcm1zIG9mIHByZWRpY3RpdmUgYWNjdXJhY3ksIGludGVycHJldGFiaWxpdHksIGFuZCByb2J1c3RuZXNzLiBBZGRpdGlvbmFsbHksIHRoZSBwcm9qZWN0IHNlZWtzIHRvIGlkZW50aWZ5IGtleSBmaW5hbmNpYWwgaW5kaWNhdG9ycyB0aGF0IGNvbnRyaWJ1dGUgdG8gYmFua3J1cHRjeSByaXNrLCBwcm92aWRpbmcgaW5zaWdodHMgZm9yIGZpbmFuY2lhbCBhbmFseXN0cyBhbmQgZGVjaXNpb24tbWFrZXJzLg0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KRGVzY3JpcHRpb24gb2YgZGF0YToNCg0KMS4gKipCYW5rcnVwdCoqIChpbnQsIHRhcmdldCB2YXJpYWJsZSkg4oCTIEluZGljYXRlcyB3aGV0aGVyIGEgY29tcGFueSBoYXMgZ29uZSBiYW5rcnVwdCAoMCA9IE5vLCAxID0gWWVzKS4gIA0KMi4gKipDdXJyZW50X1JhdGlvKiogKGZsb2F0KSDigJMgTWVhc3VyZXMgYSBjb21wYW554oCZcyBhYmlsaXR5IHRvIHBheSBzaG9ydC10ZXJtIG9ibGlnYXRpb25zIChjdXJyZW50IGFzc2V0cyAvIGN1cnJlbnQgbGlhYmlsaXRpZXMpLiAgDQozLiAqKlF1aWNrX1JhdGlvKiogKGZsb2F0KSDigJMgQSBsaXF1aWRpdHkgbWV0cmljIHRoYXQgZXhjbHVkZXMgaW52ZW50b3J5IGZyb20gY3VycmVudCBhc3NldHMgKHF1aWNrIGFzc2V0cyAvIGN1cnJlbnQgbGlhYmlsaXRpZXMpLiAgDQo0LiAqKlRvdGFsX2RlYnRfYnlfVG90YWxfbmV0d29ydGgqKiAoZmxvYXQpIOKAkyBSZXByZXNlbnRzIHRoZSBjb21wYW554oCZcyB0b3RhbCBkZWJ0IGluIHByb3BvcnRpb24gdG8gaXRzIG5ldCB3b3J0aC4gIA0KNS4gKipEZWJ0X3JhdGlvX1BlcmNlbnQqKiAoZmxvYXQpIOKAkyBUaGUgcGVyY2VudGFnZSBvZiBhIGNvbXBhbnnigJlzIGFzc2V0cyBmaW5hbmNlZCBieSBkZWJ0ICh0b3RhbCBkZWJ0IC8gdG90YWwgYXNzZXRzICogMTAwKS4gIA0KNi4gKipPcGVyYXRpbmdfcHJvZml0X2J5X1BhaWRpbl9jYXBpdGFsKiogKGZsb2F0KSDigJMgTWVhc3VyZXMgcHJvZml0YWJpbGl0eSBpbiByZWxhdGlvbiB0byBwYWlkLWluIGNhcGl0YWwuICANCjcuICoqVG90YWxfQXNzZXRfVHVybm92ZXIqKiAoZmxvYXQpIOKAkyBJbmRpY2F0ZXMgaG93IGVmZmljaWVudGx5IGEgY29tcGFueSB1dGlsaXplcyBpdHMgYXNzZXRzIHRvIGdlbmVyYXRlIHNhbGVzLiAgDQo4LiAqKldvcmtpbmdfQ2FwaXRhbF90b19Ub3RhbF9Bc3NldHMqKiAoZmxvYXQpIOKAkyBQcm9wb3J0aW9uIG9mIHdvcmtpbmcgY2FwaXRhbCBpbiB0b3RhbCBhc3NldHMsIHJlZmxlY3RpbmcgbGlxdWlkaXR5LiAgDQo5LiAqKkNhc2hfRmxvd190b19Ub3RhbF9Bc3NldHMqKiAoZmxvYXQpIOKAkyBNZWFzdXJlcyBob3cgbXVjaCBjYXNoIGZsb3cgYSBjb21wYW55IGdlbmVyYXRlcyByZWxhdGl2ZSB0byBpdHMgdG90YWwgYXNzZXRzLiAgDQoxMC4gKipDYXNoX0Zsb3dfdG9fTGlhYmlsaXR5KiogKGZsb2F0KSDigJMgUmVwcmVzZW50cyB0aGUgcHJvcG9ydGlvbiBvZiBjYXNoIGZsb3cgYXZhaWxhYmxlIHRvIGNvdmVyIGxpYWJpbGl0aWVzLiAgDQoxMS4gKipDdXJyZW50X0xpYWJpbGl0eV90b19DdXJyZW50X0Fzc2V0cyoqIChmbG9hdCkg4oCTIFJhdGlvIG9mIGN1cnJlbnQgbGlhYmlsaXRpZXMgdG8gY3VycmVudCBhc3NldHMsIGluZGljYXRpbmcgbGlxdWlkaXR5IHJpc2suICANCjEyLiAqKkdyb3NzX1Byb2ZpdF90b19TYWxlcyoqIChmbG9hdCkg4oCTIFBlcmNlbnRhZ2Ugb2YgZ3Jvc3MgcHJvZml0IGdlbmVyYXRlZCBmcm9tIHRvdGFsIHNhbGVzIChncm9zcyBwcm9maXQgLyBzYWxlcykuICANCjEzLiAqKkxldmVyYWdlX1NEKiogKGNhdGVnb3JpY2FsKSDigJMgQ2F0ZWdvcml6ZWQgYXMgIkxvdywiICJNb2RlcmF0ZSwiIG9yIG90aGVyIGxldmVscywgcmVwcmVzZW50aW5nIHRoZSBjb21wYW55J3MgbGV2ZXJhZ2Ugcmlzay4gIA0KDQoNCmBgYHtyIGRhdGEsIGluY2x1ZGU9RkFMU0V9DQojIFJlYWQgdGhlIGRhdGFzZXQgZnJvbSBhIENTViBmaWxlDQpkZiA8LSByZWFkLmNzdigiaHR0cHM6Ly9iaGF2YW5hLWRvdGNvbS5naXRodWIuaW8vUHJvamVjdDIvZGF0YS5jc3YiKSAgIyBSZXBsYWNlIHdpdGggeW91ciBmaWxlIHBhdGgNCg0KIyBTYW1wbGUgMjAwMCByb3dzDQojc2V0LnNlZWQoMTIzKSAgIyBPcHRpb25hbDogU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0KI2RmIDwtIGRhdGFbc2FtcGxlKG5yb3coZGF0YSksIDEwMDApLCBdDQoNCiMgV3JpdGUgdGhlIHNhbXBsZWQgZGF0YSB0byBhIG5ldyBDU1YgZmlsZQ0KI3dyaXRlLmNzdihkZiwgIkM6L1VzZXJzL2FnYWphL0Rvd25sb2Fkcy9TVEEgNTUyIFByb2plY3QgMi9zYW1wbGVkX2RhdGEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpICAjIFJlcGxhY2Ugd2l0aCB5b3VyIGRlc2lyZWQgb3V0cHV0IGZpbGUgcGF0aA0Kc3RyKGRmKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQojc3VtbWFyeShkZiRUb3RhbF9kZWJ0X2J5X1RvdGFsX25ldHdvcnRoKQ0KI2hpc3QoZGYkVG90YWxfZGVidF9ieV9Ub3RhbF9uZXR3b3J0aCwgYnJlYWtzID0gMzAsIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIERlYnQtdG8tTmV0IFdvcnRoIFJhdGlvIikNCm1lYW5fdmFsIDwtIG1lYW4oZGYkVG90YWxfZGVidF9ieV9Ub3RhbF9uZXR3b3J0aCwgbmEucm0gPSBUUlVFKQ0Kc2RfdmFsIDwtIHNkKGRmJFRvdGFsX2RlYnRfYnlfVG90YWxfbmV0d29ydGgsIG5hLnJtID0gVFJVRSkNCg0KZGYkTGV2ZXJhZ2VfU0QgPC0gY3V0KGRmJFRvdGFsX2RlYnRfYnlfVG90YWxfbmV0d29ydGgsIA0KICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoLUluZiwgbWVhbl92YWwgLSBzZF92YWwsIG1lYW5fdmFsICsgc2RfdmFsLCBJbmYpLCANCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJMb3ciLCAiTW9kZXJhdGUiLCAiSGlnaCIpKQ0KDQpkZiRMZXZlcmFnZV9TRCA8LSBhcy5mYWN0b3IoZGYkTGV2ZXJhZ2VfU0QpDQoNCmBgYA0KDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQoNCiMgRnVuY3Rpb24gdG8gY291bnQgbWlzc2luZyB2YWx1ZXMgaW4gYWxsIHZhcmlhYmxlcw0KY291bnRfbWlzc2luZ192YWx1ZXMgPC0gZnVuY3Rpb24oZGF0YSkgew0KICBkYXRhICU+JQ0KICAgIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfiBuYV9pZiguLCAiIikpKSAlPiUNCiAgICBzdW1tYXJpc2VfYWxsKH4gc3VtKGlzLm5hKC4pKSkgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAibWlzc2luZ19jb3VudCIpDQp9DQojIFVzZSB0aGUgZnVuY3Rpb24NCm1pc3NpbmdfdmFsdWVzIDwtIGNvdW50X21pc3NpbmdfdmFsdWVzKGRmKQ0KDQpwcmludChtaXNzaW5nX3ZhbHVlcykNCmBgYA0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0Kc3VtbWFyeShkZikNCnN0cihkZikNCmBgYA0KDQojIyBSZWxhdGlvbnNoaXAgYmV0d2VlbiBGZWF0dXJlcw0KDQoqKkNvcnJlbGF0aW9uIFBsb3QqKg0KDQpUaGUgY29ycmVsYXRpb24gbWF0cml4IGhlYXRtYXAgdmlzdWFsbHkgcmVwcmVzZW50cyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGZpbmFuY2lhbCB2YXJpYWJsZXMgYW5kIGJhbmtydXB0Y3kgc3RhdHVzLiBOb3RhYmxlIGluc2lnaHRzIGluY2x1ZGUgYSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuICoqQ3VycmVudCBSYXRpbyoqIGFuZCAqKkJhbmtydXB0Y3kgKC0wLjMyKSoqLCBpbmRpY2F0aW5nIHRoYXQgY29tcGFuaWVzIHdpdGggbG93ZXIgbGlxdWlkaXR5IGFyZSBtb3JlIGxpa2VseSB0byBnbyBiYW5rcnVwdC4gKipUb3RhbCBkZWJ0IHRvIFRvdGFsIG5ldCB3b3J0aCAoMC4zOCkqKiBhbmQgKipEZWJ0IFJhdGlvIFBlcmNlbnQgKDAuMjIpKiogc2hvdyBwb3NpdGl2ZSBjb3JyZWxhdGlvbnMgd2l0aCBiYW5rcnVwdGN5LCBzdWdnZXN0aW5nIGhpZ2hlciBsZXZlcmFnZSBpbmNyZWFzZXMgYmFua3J1cHRjeSByaXNrLiBBIHN0cm9uZyBwb3NpdGl2ZSBjb3JyZWxhdGlvbiAoKiowLjg3KiopIGV4aXN0cyBiZXR3ZWVuICoqQ2FzaCBGbG93IHRvIExpYWJpbGl0eSoqIGFuZCAqKkNhc2ggRmxvdyB0byBUb3RhbCBBc3NldHMqKiwgd2hpbGUgKipDdXJyZW50IFJhdGlvKiogYW5kICoqUXVpY2sgUmF0aW8qKiBhcmUgYWxzbyBoaWdobHkgY29ycmVsYXRlZCAoKiowLjc2KiopLCByZWZsZWN0aW5nIGludGVyZGVwZW5kZW5jaWVzIGFtb25nIGxpcXVpZGl0eSBtZXRyaWNzLiBUaGVzZSBpbnNpZ2h0cyBjYW4gYWlkIGluIGZpbmFuY2lhbCByaXNrIGFzc2Vzc21lbnQgYW5kIGJhbmtydXB0Y3kgcHJlZGljdGlvbiBtb2RlbGluZy4NCg0KYGBge3J9DQpkZl9udW0gPC0gZGYNCmRmX251bSRCYW5rcnVwdCA8LSBhcy5udW1lcmljKGFzLmZhY3RvcihkZl9udW0kQmFua3J1cHQpKQ0KbnVtZXJpY19mZWF0dXJlcyA8LSBkZl9udW0gJT4lIHNlbGVjdF9pZihpcy5udW1lcmljKQ0KIyBDb21wdXRlIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29yX21hdHJpeCA8LSBjb3IobnVtZXJpY19mZWF0dXJlcywgdXNlID0gImNvbXBsZXRlLm9icyIpDQoNCiMgUGxvdCBoZWF0bWFwDQpnZ2NvcnJwbG90KGNvcl9tYXRyaXgsIG1ldGhvZCA9ICJjaXJjbGUiLCB0eXBlID0gImxvd2VyIiwgbGFiID0gVFJVRSkNCg0KYGBgDQoNCg0KIyMgUmVsYXRpb25zaGlwIGJldHdlZW4gRmVhdHVyZXMNCg0KKipSZWxhdGlvbnNoaXAgYmV0d2VlbiBoaWdobHkgY29ycmVsYXRlZCBudW1lcmljYWwgZmVhdHVyZXMgdXNpbmcgU2NhdHRlciBQbG90KioNCg0KVGhlIHNjYXR0ZXIgcGxvdCBvZiBDYXNoIEZsb3cgdG8gVG90YWwgQXNzZXRzIChDRi9UQSkgdnMuIENhc2ggRmxvdyB0byBMaWFiaWxpdGllcyAoQ0YvTCkgcmV2ZWFscyBhIHN0cm9uZyBwb3NpdGl2ZSBjb3JyZWxhdGlvbiwgaW5kaWNhdGluZyB0aGF0IGZpcm1zIGdlbmVyYXRpbmcgaGlnaGVyIGNhc2ggZmxvdyByZWxhdGl2ZSB0byB0aGVpciBhc3NldHMgYWxzbyB0ZW5kIHRvIGhhdmUgaGlnaGVyIGNhc2ggZmxvdyByZWxhdGl2ZSB0byB0aGVpciBsaWFiaWxpdGllcy4gVGhlIG1ham9yaXR5IG9mIGRhdGEgcG9pbnRzIGNsdXN0ZXIgYWxvbmcgYSBsaW5lYXIgdHJlbmQsIHN1Z2dlc3RpbmcgYSBjb25zaXN0ZW50IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFzc2V0IHV0aWxpemF0aW9uIGFuZCBsaWFiaWxpdHkgY292ZXJhZ2UuIEEgZmV3IG91dGxpZXJzIGFib3ZlIHRoZSBtYWluIGNsdXN0ZXIgc3VnZ2VzdCBmaXJtcyB3aXRoIGV4Y2VwdGlvbmFsbHkgc3Ryb25nIGNhc2ggZmxvdyBtYW5hZ2VtZW50IG9yIGxvdyBsaWFiaWxpdGllcywgd2hpbGUgdGhvc2UgYmVsb3cgdGhlIHRyZW5kIG1heSBpbmRpY2F0ZSBjb21wYW5pZXMgc3RydWdnbGluZyB3aXRoIGRlYnQgcmVwYXltZW50LiBUaGlzIGFuYWx5c2lzIGhpZ2hsaWdodHMgdGhlIG92ZXJhbGwgZmluYW5jaWFsIGVmZmljaWVuY3kgYW5kIHBvdGVudGlhbCByaXNrIGZhY3RvcnMgd2l0aGluIHRoZSBkYXRhc2V0Lg0KDQoNCmBgYHtyfQ0KIyBTY2F0dGVyIHBsb3QgdXNpbmcgZ2dwbG90Mg0KZ2dwbG90KGRmLCBhZXMoeD1DYXNoX0Zsb3dfdG9fVG90YWxfQXNzZXRzLCB5PUNhc2hfRmxvd190b19MaWFiaWxpdHkpKSArDQogIGdlb21fcG9pbnQoY29sb3I9ImJsdWUiLCBhbHBoYT0wLjUpICsNCiAgZ2d0aXRsZSgiIENhc2ggRmxvdyB0byBUb3RhbCBBc3NldHMgdnMgQ2FzaCBGbG93IHRvIExpYWJpbGl0eSIpICsNCiAgeGxhYigiQ2FzaCBGbG93IHRvIFRvdGFsIEFzc2V0cyAiKSArDQogIHlsYWIoIkNhc2ggRmxvdyB0byBMaWFiaWxpdHkiKQ0KDQpgYGANCg0KDQoqKlJlbGF0aW9uc2hpcCBiZXR3ZWVuIFRhcmdldCBWYXJpYWJsZShCYW5rcnVwdCkgYW5kIGEgY2F0ZWdvcmljYWwgZmVhdHVyZSB1c2luZyBoZWF0bWFwKioNCg0KDQpUaGUgaGVhdG1hcCBpbGx1c3RyYXRlcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gKipsZXZlcmFnZSBsZXZlbCAoVG90YWwgRGVidCB0byBUb3RhbCBOZXQgV29ydGgpIGFuZCBiYW5rcnVwdGN5IHN0YXR1cyoqLiBUaGUgbWFqb3JpdHkgb2YgZmlybXMgY2xhc3NpZmllZCB1bmRlciB0aGUgKioiTW9kZXJhdGUiIGxldmVyYWdlIGNhdGVnb3J5KiogYXJlIG5vbi1iYW5rcnVwdCAoZGVlcCBibHVlKSwgd2hpbGUgYSBzbWFsbGVyIHBvcnRpb24gd2l0aGluIHRoaXMgY2F0ZWdvcnkgaGFzIGdvbmUgYmFua3J1cHQgKGxpZ2h0ZXIgcHVycGxlKS4gVGhlcmUgYXJlIG5vIGZpcm1zIGNhdGVnb3JpemVkIHVuZGVyICoqIkhpZ2giIGxldmVyYWdlKiosIHdoaWNoIG1pZ2h0IHN1Z2dlc3QgZGF0YSBpbWJhbGFuY2Ugb3IgYWJzZW5jZSBvZiBoaWdoLWxldmVyYWdlIGZpcm1zIGluIHRoZSBkYXRhc2V0LiBUaGlzIHZpc3VhbGl6YXRpb24gaGlnaGxpZ2h0cyB0aGF0IGNvbXBhbmllcyB3aXRoIG1vZGVyYXRlIGxldmVyYWdlIGFyZSBtb3JlIHByZXZhbGVudCwgYnV0IHNvbWUgc3RpbGwgZmFjZSBiYW5rcnVwdGN5IHJpc2tzLg0KDQoNCmBgYHtyfQ0KZGYkQmFua3J1cHQgPC0gYXMuZmFjdG9yKGRmJEJhbmtydXB0KQ0KDQpoZWF0bWFwX2RhdGEgPC0gZGYgJT4lDQogIGNvdW50KEJhbmtydXB0LCBMZXZlcmFnZV9TRCkNCg0KIyBIZWF0bWFwOiBSZWxhdGlvbnNoaXAgYmV0d2VlbiBEZWZhdWx0IGFuZCBFbXBsb3ltZW50IFN0YXR1cw0KZ2dwbG90KGhlYXRtYXBfZGF0YSwgYWVzKHggPSBCYW5rcnVwdCwgeSA9IExldmVyYWdlX1NEICwgZmlsbCA9IG4pKSArDQogIGdlb21fdGlsZSgpICsNCiAgbGFicyh0aXRsZSA9ICJIZWF0bWFwOiBCYW5rcnVwdCB2cyBMZXZlcmFnZSBMZXZlbCAoVG90YWwgRGVidCB0byBUb3RhbCBOZXQgV29ydGgpIiwNCiAgICAgICB4ID0gIkJhbmtydXB0IiwgeSA9ICJMZXZlcmFnZV9TRCIpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCioqQm94IFBsb3QqKg0KDQpUaGUgYm94IHBsb3QgY29tcGFyZXMgdGhlICoqQ3VycmVudCBSYXRpbyoqIGJldHdlZW4gYmFua3J1cHQgYW5kIG5vbi1iYW5rcnVwdCBmaXJtcy4gTm9uLWJhbmtydXB0IGZpcm1zICh0ZWFsKSB0ZW5kIHRvIGhhdmUgYSBoaWdoZXIgbWVkaWFuIGN1cnJlbnQgcmF0aW8gY29tcGFyZWQgdG8gYmFua3J1cHQgZmlybXMgKHllbGxvdyksIGluZGljYXRpbmcgYmV0dGVyIGxpcXVpZGl0eS4gVGhlIGludGVycXVhcnRpbGUgcmFuZ2UgKElRUikgZm9yIG5vbi1iYW5rcnVwdCBmaXJtcyBpcyB3aWRlciwgc3VnZ2VzdGluZyBtb3JlIHZhcmlhdGlvbiBpbiBsaXF1aWRpdHkgbGV2ZWxzLiBCYW5rcnVwdCBmaXJtcyBoYXZlIGEgbG93ZXIgbWVkaWFuIGN1cnJlbnQgcmF0aW8sIHdpdGggYSBtb3JlIGNvbXByZXNzZWQgZGlzdHJpYnV0aW9uLCBzaG93aW5nIHRoYXQgbG93ZXIgbGlxdWlkaXR5IGlzIGFzc29jaWF0ZWQgd2l0aCBmaW5hbmNpYWwgZGlzdHJlc3MuIEFkZGl0aW9uYWxseSwgYm90aCBncm91cHMgaGF2ZSBvdXRsaWVycywgYnV0IHRoZSBub24tYmFua3J1cHQgZmlybXMgZXhoaWJpdCBhIGdyZWF0ZXIgcmFuZ2Ugb2YgaGlnaGVyIGN1cnJlbnQgcmF0aW9zLg0KDQoNCmBgYHtyfQ0KDQojIEJveCBQbG90OiBSZWxhdGlvbnNoaXAgYmV0d2VlbiBCYW5rcnVwdCAoQ2F0ZWdvcmljYWwpIGFuZCAgQ3VycmVudCBSYXRpbyAoTnVtZXJpY2FsKQ0KZ2dwbG90KGRmLCBhZXMoeCA9IEJhbmtydXB0LCB5ID0gQ3VycmVudF9SYXRpbywgZmlsbCA9IEJhbmtydXB0KSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnModGl0bGUgPSAiQm94IFBsb3Q6IEJhbmtydXB0IHZzIEN1cnJlbnQgUmF0aW8iLA0KICAgICAgIHggPSAiQmFua3J1cHQgU3RhdHVzIiwgeSA9ICJDdXJyZW50IFJhdGlvIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKQ0KDQpgYGANCg0KIyMgU2tld25lc3MNCg0KDQpUaGUgc2tld25lc3MgYW5hbHlzaXMgcmV2ZWFscyB0aGF0IHNldmVyYWwgZmluYW5jaWFsIHZhcmlhYmxlcyBleGhpYml0IHNpZ25pZmljYW50IHNrZXduZXNzLCBpbmRpY2F0aW5nIGFzeW1tZXRyaWMgZGlzdHJpYnV0aW9ucy4gKipIaWdobHkgc2tld2VkIHZhcmlhYmxlcyoqIGluY2x1ZGUgKipRdWljayBSYXRpbywgVG90YWwgRGVidCB0byBUb3RhbCBOZXQgV29ydGgsIERlYnQgUmF0aW8gUGVyY2VudCwgT3BlcmF0aW5nIFByb2ZpdCBieSBQYWlkLWluIENhcGl0YWwsIFRvdGFsIEFzc2V0IFR1cm5vdmVyLCBXb3JraW5nIENhcGl0YWwgdG8gVG90YWwgQXNzZXRzLCBDYXNoIEZsb3cgdG8gVG90YWwgQXNzZXRzLCBDYXNoIEZsb3cgdG8gTGlhYmlsaXR5LCBDdXJyZW50IExpYWJpbGl0eSB0byBDdXJyZW50IEFzc2V0cywgYW5kIEdyb3NzIFByb2ZpdCB0byBTYWxlcy4qKiBOb3RhYmx5LCAqKkNhc2ggRmxvdyB0byBMaWFiaWxpdHkgKC05LjU4KSoqIGFuZCAqKlRvdGFsIERlYnQgdG8gVG90YWwgTmV0IFdvcnRoICgxNi4yMykqKiBoYXZlIGV4dHJlbWUgc2tld25lc3MgdmFsdWVzLCBzdWdnZXN0aW5nIGEgc3Ryb25nIGRldmlhdGlvbiBmcm9tIG5vcm1hbGl0eS4gVGhlc2UgZmluZGluZ3MgaW5kaWNhdGUgcG90ZW50aWFsIGRhdGEgdHJhbnNmb3JtYXRpb25zIG1heSBiZSBuZWNlc3NhcnkgZm9yIHN0YXRpc3RpY2FsIG1vZGVsaW5nLg0KDQpgYGB7cn0NCg0KDQojIFNlbGVjdCBvbmx5IG51bWVyaWMgY29sdW1ucw0KbnVtZXJpY192YXJzIDwtIGRmWywgc2FwcGx5KGRmLCBpcy5udW1lcmljKV0NCiMgQ29tcHV0ZSBza2V3bmVzcyBmb3IgZWFjaCBudW1lcmljIHZhcmlhYmxlDQpza2V3bmVzc192YWx1ZXMgPC0gc2FwcGx5KG51bWVyaWNfdmFycywgc2tld25lc3MsIG5hLnJtID0gVFJVRSkNCg0KIyBJZGVudGlmeSBoaWdobHkgc2tld2VkIHZhcmlhYmxlcw0Kc2tld2VkX3ZhcnMgPC0gbmFtZXMoc2tld25lc3NfdmFsdWVzW2Ficyhza2V3bmVzc192YWx1ZXMpID4gMV0pDQoNCiMgUHJpbnQgcmVzdWx0cw0KI3ByaW50KCJTa2V3bmVzcyB2YWx1ZXM6IikNCiNwcmludChza2V3bmVzc192YWx1ZXMpDQpwcmludCgiSGlnaGx5IHNrZXdlZCB2YXJpYWJsZXM6IikNCnByaW50KHNrZXdlZF92YXJzKQ0KIyBDcmVhdGUgYSBoaXN0b2dyYW0gZm9yIFNrZXdlZCBOdW1lcmljIFZhcmlhYmxlIC0gTm8gb2YgQ3JlZGl0IEFjY291bnRzDQojaGlzdChudW1lcmljX3ZhcnMkTm9fb2ZfY3JlZGl0X2FjYywgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTm8gb2YgQ3JlZGl0IEFjY291bnRzIiwgeGxhYiA9ICJObyBvZiBDcmVkaXQgQWNjb3VudHMiLCAgICAgIGNvbCA9ICJsaWdodGJsdWUiLCAgICAgIGJyZWFrcyA9IDMwKQ0KDQoNCmBgYA0KDQojIyBGZWF0dXJlIFRyYW5zZm9ybWF0aW9uLCBTdGFuZGFyZGl6YXRpb24gJiBOb3JtYWxpemF0aW9uDQoNCkZlYXR1cmUgdHJhbnNmb3JtYXRpb24gYW5kIHN0YW5kYXJkaXphdGlvbiBhcmUgZXNzZW50aWFsIGZvciByZWd1bGFyaXplZCByZWdyZXNzaW9uIHRlY2huaXF1ZXMgbGlrZSBSaWRnZSwgTGFzc28sIGFuZCBFbGFzdGljIE5ldCB0byBlbnN1cmUgb3B0aW1hbCBtb2RlbCBwZXJmb3JtYW5jZS4gTWFueSBmaW5hbmNpYWwgcmF0aW9zIGV4aGliaXQgaGlnaCBza2V3bmVzcywgd2hpY2ggY2FuIGRpc3RvcnQgcmVsYXRpb25zaGlwcyBhbmQgcmVkdWNlIHByZWRpY3RpdmUgYWNjdXJhY3kuIEFwcGx5aW5nIHRyYW5zZm9ybWF0aW9ucyAoc3VjaCBhcyBsb2cgb3IgQm94LUNveCkgaGVscHMgbm9ybWFsaXplIHRoZXNlIGRpc3RyaWJ1dGlvbnMsIGltcHJvdmluZyBtb2RlbCBzdGFiaWxpdHkuIEFkZGl0aW9uYWxseSwgc3RhbmRhcmRpemF0aW9uIGVuc3VyZXMgdGhhdCBhbGwgZmVhdHVyZXMgY29udHJpYnV0ZSBlcXVhbGx5IHRvIHRoZSByZWd1bGFyaXphdGlvbiBwZW5hbHR5LCBwcmV2ZW50aW5nIHZhcmlhYmxlcyB3aXRoIGxhcmdlciBzY2FsZXMgZnJvbSBkb21pbmF0aW5nIHRoZSBtb2RlbC4gVGhpcyBwcmVwcm9jZXNzaW5nIHN0ZXAgZW5oYW5jZXMgaW50ZXJwcmV0YWJpbGl0eSwgbWl0aWdhdGVzIG11bHRpY29sbGluZWFyaXR5LCBhbmQgaW1wcm92ZXMgY29udmVyZ2VuY2UgaW4gb3B0aW1pemF0aW9uIGFsZ29yaXRobXMsIGxlYWRpbmcgdG8gbW9yZSByZWxpYWJsZSBhbmQgZ2VuZXJhbGl6YWJsZSBwcmVkaWN0aW9ucy4NCg0KDQpgYGB7cn0NCg0KZGZfbm9ybV9zdGFuIDwtIGRmDQpkZl9ub3JtX3N0YW4kQmFua3J1cHQgPC0gYXMubnVtZXJpYyhhcy5mYWN0b3IoZGYkQmFua3J1cHQpKQ0KZGZfbm9ybV9zdGFuJExldmVyYWdlX1NEIDwtIGFzLm51bWVyaWMoYXMuZmFjdG9yKGRmJExldmVyYWdlX1NEKSkNCg0KIyBOb3JtYWxpemF0aW9uDQpub3JtYWxpemUgPC0gZnVuY3Rpb24oeCkgew0KICByZXR1cm4oKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpDQp9DQpkZl9ub3JtX3N0YW4kQ3VycmVudF9SYXRpb19ub3JtIDwtIG5vcm1hbGl6ZShkZiRDdXJyZW50X1JhdGlvKQ0KZGZfbm9ybV9zdGFuJFF1aWNrX1JhdGlvX25vcm0gPC0gbm9ybWFsaXplKGRmJFF1aWNrX1JhdGlvKQ0KZGZfbm9ybV9zdGFuJFRvdGFsX2RlYnRfYnlfVG90YWxfbmV0d29ydGhfbm9ybSA8LSBub3JtYWxpemUoZGYkVG90YWxfZGVidF9ieV9Ub3RhbF9uZXR3b3J0aCkNCmRmX25vcm1fc3RhbiRPcGVyYXRpbmdfcHJvZml0X2J5X1BhaWRpbl9jYXBpdGFsX25vcm0gPC0gbm9ybWFsaXplKGRmJE9wZXJhdGluZ19wcm9maXRfYnlfUGFpZGluX2NhcGl0YWwpDQpkZl9ub3JtX3N0YW4kVG90YWxfQXNzZXRfVHVybm92ZXJfbm9ybSA8LSBub3JtYWxpemUoZGYkVG90YWxfQXNzZXRfVHVybm92ZXIpDQpkZl9ub3JtX3N0YW4kQ2FzaF9GbG93X3RvX1RvdGFsX0Fzc2V0c19ub3JtIDwtIG5vcm1hbGl6ZShkZiRDYXNoX0Zsb3dfdG9fVG90YWxfQXNzZXRzKQ0KZGZfbm9ybV9zdGFuJENhc2hfRmxvd190b19MaWFiaWxpdHlfbm9ybSA8LSBub3JtYWxpemUoZGYkQ2FzaF9GbG93X3RvX0xpYWJpbGl0eSkNCmRmX25vcm1fc3RhbiRDdXJyZW50X0xpYWJpbGl0eV90b19DdXJyZW50X0Fzc2V0c19ub3JtIDwtIG5vcm1hbGl6ZShkZiRDdXJyZW50X0xpYWJpbGl0eV90b19DdXJyZW50X0Fzc2V0cykNCmRmX25vcm1fc3RhbiRHcm9zc19Qcm9maXRfdG9fU2FsZXNfbm9ybSA8LSBub3JtYWxpemUoZGYkR3Jvc3NfUHJvZml0X3RvX1NhbGVzKQ0KZGZfbm9ybV9zdGFuJERlYnRfcmF0aW9fUGVyY2VudF9ub3JtIDwtIG5vcm1hbGl6ZShkZiREZWJ0X3JhdGlvX1BlcmNlbnQpDQpkZl9ub3JtX3N0YW4kV29ya2luZ19DYXBpdGFsX3RvX1RvdGFsX0Fzc2V0cyA8LSBub3JtYWxpemUoZGYkV29ya2luZ19DYXBpdGFsX3RvX1RvdGFsX0Fzc2V0cykNCg0KDQojIExvZyB0cmFuc2Zvcm1hdGlvbg0KDQoNCmRmX25vcm1fc3RhbiRRdWlja19SYXRpb190cmFucyA8LSBsb2coZGYkUXVpY2tfUmF0aW8gKyAxKQ0KZGZfbm9ybV9zdGFuJFRvdGFsX2RlYnRfYnlfVG90YWxfbmV0d29ydGhfdHJhbnMgPC0gbG9nKGRmJFRvdGFsX2RlYnRfYnlfVG90YWxfbmV0d29ydGggKyAxKQ0KZGZfbm9ybV9zdGFuJE9wZXJhdGluZ19wcm9maXRfYnlfUGFpZGluX2NhcGl0YWxfdHJhbnMgPC0gbG9nKGRmJE9wZXJhdGluZ19wcm9maXRfYnlfUGFpZGluX2NhcGl0YWwrMSkNCmRmX25vcm1fc3RhbiRUb3RhbF9Bc3NldF9UdXJub3Zlcl90cmFucyA8LSBsb2coZGYkVG90YWxfQXNzZXRfVHVybm92ZXIrMSkNCmRmX25vcm1fc3RhbiRDYXNoX0Zsb3dfdG9fVG90YWxfQXNzZXRzX3RyYW5zIDwtIGxvZyhkZiRDYXNoX0Zsb3dfdG9fVG90YWxfQXNzZXRzKzEpDQpkZl9ub3JtX3N0YW4kQ2FzaF9GbG93X3RvX0xpYWJpbGl0eV90cmFucyA8LSBsb2coZGYkQ2FzaF9GbG93X3RvX0xpYWJpbGl0eSsxKQ0KZGZfbm9ybV9zdGFuJEN1cnJlbnRfTGlhYmlsaXR5X3RvX0N1cnJlbnRfQXNzZXRzX3RyYW5zIDwtIGxvZyhkZiRDdXJyZW50X0xpYWJpbGl0eV90b19DdXJyZW50X0Fzc2V0cysxKQ0KZGZfbm9ybV9zdGFuJEdyb3NzX1Byb2ZpdF90b19TYWxlc190cmFucyA8LSBsb2coZGYkR3Jvc3NfUHJvZml0X3RvX1NhbGVzKzEpDQpkZl9ub3JtX3N0YW4kRGVidF9yYXRpb19QZXJjZW50X3RyYW5zIDwtIGxvZyhkZiREZWJ0X3JhdGlvX1BlcmNlbnQrMSkNCmRmX25vcm1fc3RhbiRXb3JraW5nX0NhcGl0YWxfdG9fVG90YWxfQXNzZXRzX3RyYW5zIDwtIGxvZyhkZiRXb3JraW5nX0NhcGl0YWxfdG9fVG90YWxfQXNzZXRzKzEpDQoNCg0KDQojIFN0YW5kYXJkaXphdGlvbg0Kc3RhbmRhcmRpemUgPC0gZnVuY3Rpb24oeCkgew0KICByZXR1cm4oKHggLSBtZWFuKHgpKSAvIHNkKHgpKQ0KfQ0KDQpkZl9ub3JtX3N0YW4kQ3VycmVudF9SYXRpb19zdGFuZCA8LSBzdGFuZGFyZGl6ZShkZl9ub3JtX3N0YW4kQ3VycmVudF9SYXRpbykNCmRmX25vcm1fc3RhbiRRdWlja19SYXRpb19zdGFuZCA8LSBzdGFuZGFyZGl6ZShkZl9ub3JtX3N0YW4kUXVpY2tfUmF0aW9fdHJhbnMpDQpkZl9ub3JtX3N0YW4kVG90YWxfZGVidF9ieV9Ub3RhbF9uZXR3b3J0aF9zdGFuZCA8LSBzdGFuZGFyZGl6ZShkZl9ub3JtX3N0YW4kVG90YWxfZGVidF9ieV9Ub3RhbF9uZXR3b3J0aF90cmFucykNCmRmX25vcm1fc3RhbiRPcGVyYXRpbmdfcHJvZml0X2J5X1BhaWRpbl9jYXBpdGFsX3N0YW5kIDwtIHN0YW5kYXJkaXplKGRmX25vcm1fc3RhbiRPcGVyYXRpbmdfcHJvZml0X2J5X1BhaWRpbl9jYXBpdGFsX3RyYW5zKQ0KZGZfbm9ybV9zdGFuJFRvdGFsX0Fzc2V0X1R1cm5vdmVyX3N0YW5kIDwtIHN0YW5kYXJkaXplKGRmX25vcm1fc3RhbiRUb3RhbF9Bc3NldF9UdXJub3Zlcl90cmFucykNCmRmX25vcm1fc3RhbiRDYXNoX0Zsb3dfdG9fVG90YWxfQXNzZXRzX3N0YW5kIDwtIHN0YW5kYXJkaXplKGRmX25vcm1fc3RhbiRDYXNoX0Zsb3dfdG9fVG90YWxfQXNzZXRzX3RyYW5zKQ0KZGZfbm9ybV9zdGFuJENhc2hfRmxvd190b19MaWFiaWxpdHlfc3RhbmQgPC0gc3RhbmRhcmRpemUoZGZfbm9ybV9zdGFuJENhc2hfRmxvd190b19MaWFiaWxpdHlfdHJhbnMpDQpkZl9ub3JtX3N0YW4kQ3VycmVudF9MaWFiaWxpdHlfdG9fQ3VycmVudF9Bc3NldHNfc3RhbmQgPC0gc3RhbmRhcmRpemUoZGZfbm9ybV9zdGFuJEN1cnJlbnRfTGlhYmlsaXR5X3RvX0N1cnJlbnRfQXNzZXRzX3RyYW5zKQ0KZGZfbm9ybV9zdGFuJEdyb3NzX1Byb2ZpdF90b19TYWxlc19zdGFuZCA8LSBzdGFuZGFyZGl6ZShkZl9ub3JtX3N0YW4kR3Jvc3NfUHJvZml0X3RvX1NhbGVzX3RyYW5zKQ0KZGZfbm9ybV9zdGFuJERlYnRfcmF0aW9fUGVyY2VudF9zdGFuZCA8LSBzdGFuZGFyZGl6ZShkZl9ub3JtX3N0YW4kRGVidF9yYXRpb19QZXJjZW50X3RyYW5zKQ0KZGZfbm9ybV9zdGFuJFdvcmtpbmdfQ2FwaXRhbF90b19Ub3RhbF9Bc3NldHNfc3RhbmQgPC0gc3RhbmRhcmRpemUoZGZfbm9ybV9zdGFuJFdvcmtpbmdfQ2FwaXRhbF90b19Ub3RhbF9Bc3NldHNfdHJhbnMpDQoNCg0KI2Z1bGxfbW9kZWwgPC0gZ2xtKERlZmF1bHQgfiAuLCBkYXRhID0gbmV3X2RmLCBmYW1pbHkgPSBiaW5vbWlhbCkgIA0KI3N1bW1hcnkoZnVsbF9tb2RlbCkNCg0KI2ZpbmFsX21vZGVsIDwtIGdsbShEZWZhdWx0IH4gQ2hlY2tpbmdfYW1vdW50K1Rlcm0rQ3JlZGl0X3Njb3JlK0FnZSwgZGF0YSA9IG5ld19kZiwgZmFtaWx5ID0gYmlub21pYWwgKQ0KI3N1bW1hcnkoZmluYWxfbW9kZWwpDQoNCiMgS2VlcCBvbmx5IHN0YW5kYXJkaXplZCBjb2x1bW5zIGFuZCB0YXJnZXQgdmFyaWFibGUNCmRmX3N0YW4gPC0gZGZfbm9ybV9zdGFuICU+JSBzZWxlY3QoQmFua3J1cHQsIExldmVyYWdlX1NELCBlbmRzX3dpdGgoIl9zdGFuZCIpKQ0KDQojIFZpZXcgc3RydWN0dXJlIG9mIHRoZSBzdGFuZGFyZGl6ZWQgZGF0YXNldA0KI3N0cihkZl9zdGFuKQ0KDQpgYGANCg0KIyMgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcw0KDQpUaGUgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKSByZXN1bHRzIGluZGljYXRlIHRoYXQgdGhlIGZpcnN0IGZldyBwcmluY2lwYWwgY29tcG9uZW50cyBjYXB0dXJlIG1vc3Qgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkYXRhc2V0LCB3aXRoIFBDMSBhbG9uZSBleHBsYWluaW5nIDI3LjclIGFuZCB0aGUgZmlyc3QgZml2ZSBjb21wb25lbnRzIGFjY291bnRpbmcgZm9yIGFwcHJveGltYXRlbHkgNzAuNyUgb2YgdGhlIHRvdGFsIHZhcmlhbmNlLiBUaGUgc2NyZWUgcGxvdCBmdXJ0aGVyIGNvbmZpcm1zIHRoYXQgYmV5b25kIGEgY2VydGFpbiBudW1iZXIgb2YgY29tcG9uZW50cywgdGhlIHZhcmlhbmNlIGV4cGxhaW5lZCBkcm9wcyBzaWduaWZpY2FudGx5LCBzdWdnZXN0aW5nIHRoYXQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGlzIGZlYXNpYmxlLiBUaGUgc2NhdHRlciBwbG90IG9mIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgc2hvd3Mgc29tZSBjbHVzdGVyaW5nIGJ1dCBhbHNvIGhpZ2hsaWdodHMgb3V0bGllcnMgdGhhdCBtYXkgaW5mbHVlbmNlIHRoZSBhbmFseXNpcy4gT3ZlcmFsbCwgYXBwbHlpbmcgUENBIGluIHRoaXMgcHJvamVjdCBpcyBiZW5lZmljaWFsIGFzIGl0IGhlbHBzIHJlZHVjZSBkaW1lbnNpb25hbGl0eSB3aGlsZSByZXRhaW5pbmcgbW9zdCBvZiB0aGUgaW1wb3J0YW50IGluZm9ybWF0aW9uLCBpbXByb3ZpbmcgbW9kZWwgZWZmaWNpZW5jeSBhbmQgaW50ZXJwcmV0YWJpbGl0eSwgZXNwZWNpYWxseSB3aGVuIGRlYWxpbmcgd2l0aCBoaWdobHkgY29ycmVsYXRlZCBhbmQgc2tld2VkIHZhcmlhYmxlcy4NCg0KYGBge3J9DQoNCiMgU3RlcCAxOiBDb252ZXJ0IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0byBudW1lcmljIHVzaW5nIG9uZS1ob3QgZW5jb2RpbmcNCmRmX251bWVyaWMgPC0gZGYgJT4lDQogbXV0YXRlKGFjcm9zcyhjKEJhbmtydXB0LCBMZXZlcmFnZV9TRCksIGFzLmZhY3RvcikpICU+JSAgIyBDb252ZXJ0IHRvIGZhY3RvcnMNCiAgIGR1bW15VmFycyh+IC4sIGRhdGEgPSAuKSAlPiUgICAgICANCiAgcHJlZGljdChuZXdkYXRhID0gZGYpICU+JSANCiAgIGFzLmRhdGEuZnJhbWUoKQ0KIA0KICMgU3RlcCAyOiBTZWxlY3Qgb25seSBudW1lcmljIGZlYXR1cmVzDQogbnVtZXJpY19kYXRhIDwtIGRmX251bWVyaWMgJT4lIHNlbGVjdF9pZihpcy5udW1lcmljKQ0KIA0KICMgU3RlcCAzOiBIYW5kbGUgbWlzc2luZyBhbmQgaW5maW5pdGUgdmFsdWVzDQogbnVtZXJpY19kYXRhW2lzLm5hKG51bWVyaWNfZGF0YSldIDwtIDAgICAjIFJlcGxhY2UgTkFzIHdpdGggMCAob3IgdXNlIG1lZGlhbiBpbXB1dGF0aW9uKQ0KIA0KIGNvbnN0YW50X2NvbHMgPC0gYXBwbHkobnVtZXJpY19kYXRhLCAyLCBmdW5jdGlvbihjb2wpIHZhcihjb2wsIG5hLnJtID0gVFJVRSkgPT0gMCkNCm51bWVyaWNfZGF0YSA8LSBudW1lcmljX2RhdGFbLCAhY29uc3RhbnRfY29sc10NCg0KIA0KICMgU3RlcCA0OiBTdGFuZGFyZGl6ZSB0aGUgZGF0YSAob25seSBhZnRlciBoYW5kbGluZyBtaXNzaW5nIHZhbHVlcykNCiBzY2FsZWRfZGF0YSA8LSBzY2FsZShudW1lcmljX2RhdGEpDQogDQogIyBTdGVwIDU6IFBlcmZvcm0gUENBDQogcGNhX3Jlc3VsdCA8LSBwcmNvbXAoc2NhbGVkX2RhdGEsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkNCiANCiBzdW1tYXJ5KHBjYV9yZXN1bHQpDQogDQogIyBTdGVwIDY6IFNjcmVlIFBsb3QgKHRvIGRlY2lkZSBudW1iZXIgb2YgY29tcG9uZW50cyB0byBrZWVwKQ0KIHNjcmVlcGxvdChwY2FfcmVzdWx0LCB0eXBlID0gImxpbmVzIiwgbWFpbiA9ICJTY3JlZSBQbG90IikNCiANCiAjIFN0ZXAgNzogQmlwbG90IChQQ0EgdmlzdWFsaXphdGlvbikNCiMgYmlwbG90KHBjYV9yZXN1bHQsIHNjYWxlID0gMCkNCiANCiAjIFN0ZXAgODogU2NhdHRlciBwbG90IG9mIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cw0KIHBjYV9kZiA8LSBhcy5kYXRhLmZyYW1lKHBjYV9yZXN1bHQkeCkNCiBnZ3Bsb3QocGNhX2RmLCBhZXMoeCA9IFBDMSwgeSA9IFBDMikpICsNCiAgIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43LCBjb2xvciA9ICJibHVlIikgKw0KIGxhYnModGl0bGUgPSAiUENBIC0gRmlyc3QgVHdvIFByaW5jaXBhbCBDb21wb25lbnRzIikgKw0KICAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQoNCiMjIFJlZ3VsYXJpemF0aW9uDQoNCisgUmVndWxhcml6ZWQgTG9naXN0aWMgUmVncmVzc2lvbjoNCg0KDQpUaGUgcmVzdWx0cyBmcm9tIHRoZSBMQVNTTyByZWd1bGFyaXplZCByZWdyZXNzaW9uIGFuYWx5c2lzIHByb3ZpZGUga2V5IGluc2lnaHRzIGludG8gZmVhdHVyZSBzZWxlY3Rpb24gYW5kIG1vZGVsIHBlcmZvcm1hbmNlLg0KDQpDb2VmZmljaWVudCBQYXRoIFBsb3QgKExlZnQgUGFuZWwpOiBUaGlzIHBsb3Qgc2hvd3MgaG93IHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBzaHJpbmsgYXMgdGhlIHJlZ3VsYXJpemF0aW9uIHBhcmFtZXRlciAozrspIGluY3JlYXNlcy4gSW5pdGlhbGx5LCBhdCBsb3cgzrsgdmFsdWVzIChyaWdodCBzaWRlIG9mIHRoZSBwbG90KSwgbW9zdCBwcmVkaWN0b3JzIGhhdmUgbm9uemVybyBjb2VmZmljaWVudHMsIG1lYW5pbmcgdGhleSBhcmUgYWN0aXZlbHkgY29udHJpYnV0aW5nIHRvIHRoZSBtb2RlbC4gQXMgzrsgaW5jcmVhc2VzIChtb3ZpbmcgbGVmdCksIG1hbnkgY29lZmZpY2llbnRzIHNocmluayB0b3dhcmQgemVybywgZGVtb25zdHJhdGluZyBMQVNTT+KAmXMgYWJpbGl0eSB0byBlbmZvcmNlIHNwYXJzaXR5IGJ5IHNlbGVjdGluZyBvbmx5IHRoZSBtb3N0IGltcG9ydGFudCBmZWF0dXJlcy4gVGhlIHJlZCBkYXNoZWQgbGluZSByZXByZXNlbnRzIHRoZSDOuyB2YWx1ZSB0aGF0IG1pbmltaXplcyBjcm9zcy12YWxpZGF0aW9uIGVycm9yLCBzdHJpa2luZyBhIGJhbGFuY2UgYmV0d2VlbiBtb2RlbCBjb21wbGV4aXR5IGFuZCBwcmVkaWN0aXZlIHBvd2VyLiBUaGUgYmx1ZSBkYXNoZWQgbGluZSByZXByZXNlbnRzIHRoZSBtb3N0IHJlZ3VsYXJpemVkIG1vZGVsIHdpdGhpbiBvbmUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1pbmltdW0gY3Jvc3MtdmFsaWRhdGlvbiBlcnJvciwgb2Z0ZW4gY2hvc2VuIGZvciBiZXR0ZXIgZ2VuZXJhbGl6YXRpb24uDQoNCk1vZGVsIEZpdCBQbG90IChSaWdodCBQYW5lbCk6IFRoaXMgcGxvdCBpbGx1c3RyYXRlcyB0aGUgYmlub21pYWwgZGV2aWFuY2UgKGEgbWVhc3VyZSBvZiBtb2RlbCBlcnJvcikgZm9yIGRpZmZlcmVudCDOuyB2YWx1ZXMuIFRoZSBjdXJ2ZSBpbml0aWFsbHkgZGVjcmVhc2VzLCBpbmRpY2F0aW5nIHRoYXQgbW9kZXJhdGUgcmVndWxhcml6YXRpb24gaW1wcm92ZXMgbW9kZWwgcGVyZm9ybWFuY2UgYnkgcmVkdWNpbmcgb3ZlcmZpdHRpbmcuIFRoZSByZWQgcG9pbnRzIHJlcHJlc2VudCBtZWFuIGRldmlhbmNlIHZhbHVlcywgd2l0aCBlcnJvciBiYXJzIHNob3dpbmcgdmFyaWFiaWxpdHkuIFRoZSBkZXZpYW5jZSByZWFjaGVzIGl0cyBsb3dlc3QgcG9pbnQgYXJvdW5kIM674omI4oiSNSAobG9nIHNjYWxlKSwgYWZ0ZXIgd2hpY2ggaXQgaW5jcmVhc2VzIGFzIGV4Y2Vzc2l2ZSByZWd1bGFyaXphdGlvbiByZW1vdmVzIHRvbyBtYW55IGltcG9ydGFudCBmZWF0dXJlcywgbGVhZGluZyB0byB1bmRlcmZpdHRpbmcuDQoNCg0KYGBge3J9DQoNCg0KDQpkZiA8LSBkZl9zdGFuDQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byBwcmVkaWN0b3JzIChYKSBhbmQgcmVzcG9uc2UgKHkpDQpYIDwtIG1vZGVsLm1hdHJpeChCYW5rcnVwdCB+IC4sIGRmX3N0YW4pWywtMV0gICMgUmVtb3ZlIHRoZSBpbnRlcmNlcHQgY29sdW1uDQp5IDwtIGRmJEJhbmtydXB0DQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQpzZXQuc2VlZCgxMjMpDQp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSwgcCA9IDAuOCwgbGlzdCA9IEZBTFNFKQ0KWF90cmFpbiA8LSBYW3RyYWluSW5kZXgsIF0NClhfdGVzdCA8LSBYWy10cmFpbkluZGV4LCBdDQp5X3RyYWluIDwtIHlbdHJhaW5JbmRleF0NCnlfdGVzdCA8LSB5Wy10cmFpbkluZGV4XQ0KDQojdGFibGUoeV90ZXN0KQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgRml0IExBU1NPIG1vZGVsDQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KbGFzc29fbW9kZWwgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMSkNCg0KIyBDcm9zcy12YWxpZGF0aW9uIHRvIGZpbmQgdGhlIG9wdGltYWwgbGFtYmRhDQpjdl9sYXNzbyA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAxKQ0KDQojIE9wdGltYWwgbGFtYmRhDQpsYW1iZGFfbGFzc28gPC0gY3ZfbGFzc28kbGFtYmRhLm1pbg0KDQojIFJlZml0IHRoZSBtb2RlbCB3aXRoIHRoZSBvcHRpbWFsIGxhbWJkYQ0KbGFzc29fbW9kZWxfb3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSA9IGxhbWJkYV9sYXNzbykNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIyBWaXN1YWxpemUgdGhlIGltcGFjdCBvZiBsYW1iZGEgb24gc2hyaW5raW5nIGNvZWZmaWNpZW50cw0KIyBQbG90IGNvZWZmaWNpZW50IHBhdGhzDQpwYXIobWFyPWMoNSw0LDYsMiksIG1mcm93PWMoMSwyKSkgIyANCnBsb3QobGFzc29fbW9kZWwsIHh2YXIgPSAibGFtYmRhIiwgbGFiZWwgPSBUUlVFLA0KICAgICBjb2wgPSByYWluYm93KDgpLA0KICAgICBsd2QgPSAxLA0KICAgICBtYWluID0gIkNvZWZmaWNpZW50IFBhdGggUGxvdDogTEFTU08iLA0KICAgICBjZXgubWFpbiA9IDAuOCkNCnRleHQoLTYsIDAuNCwgIm1pbmltdW0gQ1YgZXJyb3IiLCBjb2w9InJlZCIsIGNleCA9IDAuNiApDQphYmxpbmUodiA9IGxvZyhjdl9sYXNzbyRsYW1iZGEubWluKSwgY29sID0gInJlZCIsIGx0eSA9IDQsIGx3ZCA9IDEpDQphYmxpbmUodiA9IGxvZyhjdl9sYXNzbyRsYW1iZGEuMXNlKSwgY29sID0gImJsdWUiLCBsdHkgPSA0LCBsd2QgPSAxKQ0KDQpwbG90KGN2X2xhc3NvLCBtYWluPSJNZWFzdXJlIG9mIE1vZGVsIEZpdDogTEFTU08iLCBjZXgubWFpbiA9IDAuOCkNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEZpdCBSaWRnZSBtb2RlbA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCnJpZGdlX21vZGVsIDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDApDQoNCiMgQ3Jvc3MtdmFsaWRhdGlvbiB0byBmaW5kIHRoZSBvcHRpbWFsIGxhbWJkYQ0KY3ZfcmlkZ2UgPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMCkNCg0KIyBPcHRpbWFsIGxhbWJkYQ0KbGFtYmRhX3JpZGdlIDwtIGN2X3JpZGdlJGxhbWJkYS5taW4NCg0KIyBSZWZpdCB0aGUgbW9kZWwgd2l0aCB0aGUgb3B0aW1hbCBsYW1iZGENCnJpZGdlX21vZGVsX29wdCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBsYW1iZGFfcmlkZ2UpDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgRml0IEVsYXN0aWMgTmV0IG1vZGVsIChlLmcuLCBhbHBoYSA9IDAuNSkNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQplbGFzdGljX21vZGVsIDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDAuNSkNCg0KIyBDcm9zcy12YWxpZGF0aW9uIHRvIGZpbmQgdGhlIG9wdGltYWwgbGFtYmRhDQpjdl9lbGFzdGljIDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDAuNSkNCg0KIyBPcHRpbWFsIGxhbWJkYQ0KbGFtYmRhX2VsYXN0aWMgPC0gY3ZfZWxhc3RpYyRsYW1iZGEubWluDQoNCiMgUmVmaXQgdGhlIG1vZGVsIHdpdGggdGhlIG9wdGltYWwgbGFtYmRhDQplbGFzdGljX21vZGVsX29wdCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSA9IGxhbWJkYV9lbGFzdGljKQ0KDQpgYGANCg0KYGBge3J9DQoNCmxhc3NvLmNvZWYgPC0gYXMubWF0cml4KGNvZWYobGFzc29fbW9kZWxfb3B0KSkNCnJpZGdlLmNvZWYgPC0gYXMubWF0cml4KGNvZWYocmlkZ2VfbW9kZWxfb3B0KSkNCmVsYXN0aWMuY29lZiA8LSBhcy5tYXRyaXgoY29lZihlbGFzdGljX21vZGVsX29wdCkpDQpyZWd1bGFyaXplZC5jb2VmIDwtIGRhdGEuZnJhbWUobGFzc28gPSBsYXNzby5jb2VmWywxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWRnZSA9IHJpZGdlLmNvZWZbLDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBlbGFzdGljbmV0ID0gZWxhc3RpYy5jb2VmWywxXSkNCnBhbmRlcihyZWd1bGFyaXplZC5jb2VmKQ0KDQpgYGANCg0KIyMgT3B0aW1hbCBDdXR0b2ZmIFByb2JhYmlsaXR5IERldGVybWluYXRpb24NCg0KT3B0aW1hbCBDdXRvZmYgUHJvYmFiaWxpdHkgZm9yIHRoaXMgbW9kZWwgaXMgMCBmb3IgdGhlIHJlYXNvbnMgYmVsb3c6DQoNCkNsYXNzIEltYmFsYW5jZTogQSBoaWdobHkgaW1iYWxhbmNlZCBkYXRhc2V0IG1heSBjYXVzZSB0aGUgbW9kZWwgdG8gZmF2b3IgdGhlIG1ham9yaXR5IGNsYXNzLCBsZWFkaW5nIHRvIGFuIG9wdGltYWwgY3V0LW9mZiBvZiAwIGZvciBtYXhpbXVtIGFjY3VyYWN5Lg0KDQpPdmVyLVJlZ3VsYXJpemF0aW9uOiBTdHJvbmcgcmVndWxhcml6YXRpb24gKGhpZ2ggbGFtYmRhIHZhbHVlcykgY2FuIHNocmluayBjb2VmZmljaWVudHMsIHJlZHVjaW5nIHRoZSBtb2RlbOKAmXMgYWJpbGl0eSB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gY2xhc3Nlcy4NCg0KQmlhcyBpbiBQcm9iYWJpbGl0eSBFc3RpbWF0ZXM6IElmIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGFyZSBza2V3ZWQgdG93YXJkIGhpZ2hlciB2YWx1ZXMsIHRoZSBtb2RlbCBtYXkgY2xhc3NpZnkgbW9zdCBpbnN0YW5jZXMgYXMgdGhlIHBvc2l0aXZlIGNsYXNzLCBtYWtpbmcgMCB0aGUgYmVzdCBjdXQtb2ZmLg0KDQpBY2N1cmFjeSBhcyBhIE1pc2xlYWRpbmcgTWV0cmljOiBTaW5jZSBhY2N1cmFjeSBkb2VzIG5vdCBhY2NvdW50IGZvciBjbGFzcyBkaXN0cmlidXRpb24sIGFsdGVybmF0aXZlIG1ldHJpY3Mgc3VjaCBhcyBBVUMtUk9DLCBwcmVjaXNpb24tcmVjYWxsLCBhbmQgRjEtc2NvcmUgc2hvdWxkIGJlIGNvbnNpZGVyZWQgZm9yIGJldHRlciB0aHJlc2hvbGQgc2VsZWN0aW9uLg0KDQpgYGB7cn0NCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBQcmVkaWN0IG9uIHRoZSB0ZXN0IHNldDogdHlwZSA9ICJjbGFzcyIgdXNlcyB0aGUgZGVmYXVsdCANCiMgY3V0LW9mZiBwcm9iYWJpbGl0eSB0byBiZSAwLjUuDQpwcmVkaWN0X2xhc3NvIDwtIHByZWRpY3QobGFzc29fbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3RfcmlkZ2UgPC0gcHJlZGljdChyaWRnZV9tb2RlbF9vcHQsIG5ld3ggPSBYX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZGljdF9lbGFzdGljIDwtIHByZWRpY3QoZWxhc3RpY19tb2RlbF9vcHQsIG5ld3ggPSBYX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIE9wdGltYWwgY3V0b2ZmIHByb2JhYmlsaXR5IGRldGVybWluYXRpb24NCnNlcS5jdXQgPC0gc2VxKDAsMSwgbGVuZ3RoPTUwKQ0KIyB5IGlzIGEgdmVjdG9yIG9mIDAgYW5kIDENCmFjYy5sYXNzbyA8LSBOVUxMDQphY2MucmlkZ2UgPC0gTlVMTA0KYWNjLmVsYXN0aWMgPC0gTlVMTA0KZm9yIChpIGluIDE6bGVuZ3RoKHNlcS5jdXQpKXsNCiAgIHByZWR5Lmxhc3NvIDwtIGlmZWxzZShwcmVkaWN0X2xhc3NvID5zZXEuY3V0W2ldLCAxLCAwKQ0KICAgcHJlZHkucmlkZ2U8LSBpZmVsc2UocHJlZGljdF9yaWRnZSA+c2VxLmN1dFtpXSwgMSwgMCkNCiAgIHByZWR5LmVsYXN0aWM8LSBpZmVsc2UocHJlZGljdF9lbGFzdGljID5zZXEuY3V0W2ldLCAxLCAwKQ0KICAgIyMNCiAgIGFjYy5sYXNzb1tpXSA8LSBtZWFuKHlfdGVzdCAgPT0gcHJlZHkubGFzc28pDQogICBhY2MucmlkZ2VbaV0gPC0gbWVhbih5X3Rlc3QgPT0gcHJlZHkucmlkZ2UpDQogICBhY2MuZWxhc3RpY1tpXSA8LSBtZWFuKHlfdGVzdCAgPT0gcHJlZHkuZWxhc3RpYykNCn0NCiMjIG9wdGltYWwgY3V0LW9mZjogaWYgdGhlIG1heGltdW0gYWNjdXJhY3kgb2NjdXJzIGF0IG11bHRpcGxlDQojIyBjdXQtb2ZmIHByb2JhYmlsaXRpZXMsIHRoZSBhdmVyYWdlIG9mIHRoZXNlIGN1dG9mZiBwcm9iYWJpbGl0aWVzDQojIyB3aWxsIGJlIGRlZmluZWQgYXMgdGhlIG9wdGltYWwgY3V0b2ZmIHByb2JhYmlsaXR5DQpvcHQuY3V0Lmxhc3NvIDwtIG1lYW4oc2VxLmN1dFt3aGljaChhY2MubGFzc289PW1heChhY2MubGFzc28pKV0pDQpvcHQuY3V0LnJpZGdlPC0gbWVhbihzZXEuY3V0W3doaWNoKGFjYy5yaWRnZT09bWF4KGFjYy5yaWRnZSkpXSkNCm9wdC5jdXQuZWxhc3RpYyA8LSBtZWFuKHNlcS5jdXRbd2hpY2goYWNjLmVsYXN0aWM9PW1heChhY2MuZWxhc3RpYykpXSkNCiMjDQoNCg0KDQojIFByaW50IG9wdGltYWwgY3V0b2ZmIHByb2JhYmlsaXRpZXMNCiNjYXQoIk9wdGltYWwgQ3V0b2ZmIGZvciBMQVNTTzoiLCBvcHQuY3V0Lmxhc3NvLCAiXG4iKQ0KI2NhdCgiT3B0aW1hbCBDdXRvZmYgZm9yIFJpZGdlOiIsIG9wdC5jdXQucmlkZ2UsICJcbiIpDQojY2F0KCJPcHRpbWFsIEN1dG9mZiBmb3IgRWxhc3RpYyBOZXQ6Iiwgb3B0LmN1dC5lbGFzdGljLCAiXG4iKQ0KDQphY2MuZGF0YSA8LSBkYXRhLmZyYW1lKHByb2IgPSByZXAoc2VxLmN1dCwzKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGFjYz1jKGFjYy5sYXNzbywgYWNjLnJpZGdlLCBhY2MuZWxhc3RpYyksIA0KICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGMocmVwKCJsYXNzbyIsNTApLCByZXAoInJpZGdlIiw1MCksIHJlcCgiZWxhc3RpYyIsNTApKSkNCg0KYGBgDQoNCg0KIyMgQWNjdXJhY3kgUGxvdA0KDQpUaGUgYWNjdXJhY3kgdnMuIGN1dC1vZmYgcHJvYmFiaWxpdHkgcGxvdCBpbmRpY2F0ZXMgdGhhdCBhbGwgdGhyZWUgbW9kZWxz4oCUTEFTU08sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXTigJRleGhpYml0IG5lYXJseSBpZGVudGljYWwgY2xhc3NpZmljYXRpb24gcGVyZm9ybWFuY2UsIGFjaGlldmluZyBtYXhpbXVtIGFjY3VyYWN5ICh+MC43NTExKSBhdCBhIGxvdyBjdXQtb2ZmIHRocmVzaG9sZC4gQXMgdGhlIGN1dC1vZmYgaW5jcmVhc2VzLCBhY2N1cmFjeSBkZWNsaW5lcyBzaGFycGx5LCBzdWdnZXN0aW5nIGEgc3Ryb25nIGNsYXNzIGltYmFsYW5jZSBvciB0aHJlc2hvbGQgc2Vuc2l0aXZpdHkuIFRoZSBtb2RlbHMgcHJlZG9taW5hbnRseSBmYXZvciB0aGUgbWFqb3JpdHkgY2xhc3MgYXQgbG93ZXIgdGhyZXNob2xkcywgbGVhZGluZyB0byBoaWdoZXIgYWNjdXJhY3kgYnV0IHBvdGVudGlhbGx5IHBvb3Igc2Vuc2l0aXZpdHkgZm9yIG1pbm9yaXR5IGNsYXNzIGRldGVjdGlvbi4gVGhlIHJhcGlkIGRlY2xpbmUgaW4gYWNjdXJhY3kgYmV5b25kIGEgMC4xIGN1dC1vZmYgc3VnZ2VzdHMgdGhhdCBtaXNjbGFzc2lmaWNhdGlvbiByYXRlcyBpbmNyZWFzZSBzaWduaWZpY2FudGx5LiBUaGlzIGluZGljYXRlcyB0aGF0IGFjY3VyYWN5IGFsb25lIG1heSBub3QgYmUgdGhlIGJlc3QgZXZhbHVhdGlvbiBtZXRyaWMsIHdhcnJhbnRpbmcgYWRkaXRpb25hbCBhc3Nlc3NtZW50cyBzdWNoIGFzIEFVQy1ST0MgYW5kIEYxLXNjb3JlLiBPcHRpbWl6aW5nIHRoZSB0aHJlc2hvbGQgdXNpbmcgdGhlc2UgYWx0ZXJuYXRpdmUgbWV0cmljcyBjb3VsZCBlbmhhbmNlIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuDQoNCg0KYGBge3J9DQoNCiMjDQpnZy5hY2MgPC0gZ2dwbG90KGRhdGEgPSBhY2MuZGF0YSwgYWVzKHg9cHJvYiwgeSA9IGFjYywgY29sb3IgPSBncm91cCkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjYsIHkgPSAwLjQ1LCANCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSggIkFjY3VyYWN5OiAiLCByb3VuZChtYXgoYWNjLmxhc3NvKSw1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlxuQWNjdXJhY3k6ICIsIHJvdW5kKG1heChhY2MucmlkZ2UpLDUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAiXG5BY2N1cmFjeTogIiwgcm91bmQobWF4KGFjYy5lbGFzdGljKSw1KSksIA0KICAgICAgICAgICBzaXplID0gMywgDQogICAgICAgICAgIGNvbG9yID0gIm5hdnkiKSArDQogIGdndGl0bGUoIkN1dC1vZmYgUHJvYmFiaWxpdHkgdnMgQWNjdXJhY3kiKSArDQogIGxhYnMoeCA9ICJjdXQtb2ZmIFByb2JhYmlsaXR5IiwgDQogICAgICAgeSA9ICJhY2N1cmFjeSIsIGNvbG9yID0gIkdyb3VwIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyMNCmdncGxvdGx5KGdnLmFjYykNCg0KDQpgYGANCg0KDQoNCiMjIENvbmZ1c2lvbiBNYXRyaXgNCg0KVGhlIHBlcmZvcm1hbmNlIG1ldHJpY3MgZm9yIExBU1NPLCBSaWRnZSwgYW5kIEVsYXN0aWMgTmV0IHJlZ3Jlc3Npb24gbW9kZWxzIGluZGljYXRlIHN0cm9uZyByZWNhbGwgKDAuOTcwNCkgYWNyb3NzIGFsbCBtb2RlbHMsIHN1Z2dlc3RpbmcgdGhhdCB0aGV5IGVmZmVjdGl2ZWx5IGlkZW50aWZ5IHRoZSBwb3NpdGl2ZSBjbGFzcy4gSG93ZXZlciwgc3BlY2lmaWNpdHkgaXMgcmVsYXRpdmVseSBsb3cgKDAuMzkyOeKAkzAuMzc1KSwgbWVhbmluZyB0aGUgbW9kZWxzIHN0cnVnZ2xlIHRvIGNvcnJlY3RseSBjbGFzc2lmeSBuZWdhdGl2ZSBpbnN0YW5jZXMsIGxpa2VseSBkdWUgdG8gY2xhc3MgaW1iYWxhbmNlLiBQcmVjaXNpb24gdmFsdWVzICh+MC44MikgYW5kIEYxLXNjb3JlcyAofjAuODkpIGNvbmZpcm0gYSBnb29kIGJhbGFuY2UgYmV0d2VlbiBwcmVjaXNpb24gYW5kIHJlY2FsbCwgZmF2b3Jpbmcgc2Vuc2l0aXZpdHkuIFRoZSBiYWxhbmNlZCBhY2N1cmFjeSB2YWx1ZXMgKDAuNjgxNiBmb3IgTEFTU08sIDAuNjYzOCBmb3IgUmlkZ2UsIGFuZCAwLjY3MjcgZm9yIEVsYXN0aWMgTmV0KSBpbmRpY2F0ZSBtb2RlcmF0ZSBvdmVyYWxsIHBlcmZvcm1hbmNlLCB3aXRoIExBU1NPIHNsaWdodGx5IG91dHBlcmZvcm1pbmcgdGhlIG90aGVyIG1vZGVscy4gVGhlc2UgcmVzdWx0cyBzdWdnZXN0IHRoYXQgd2hpbGUgdGhlIG1vZGVscyBhcmUgc3Ryb25nIGluIGRldGVjdGluZyBwb3NpdGl2ZXMsIGZ1cnRoZXIgdGhyZXNob2xkIHR1bmluZyBvciBhbHRlcm5hdGl2ZSBldmFsdWF0aW9uIG1ldHJpY3MgKHN1Y2ggYXMgQVVDLVJPQykgbWF5IGltcHJvdmUgY2xhc3NpZmljYXRpb24gb2YgbmVnYXRpdmUgY2FzZXMuDQoNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyB1c2luZyB0aGUgb3B0aW1hbCBjdXRvZmYgcHJvYmFiaWxpdHkgdG8gcHJlZGljdCBsYWJlbHMNCiMjIA0KI3ByZWQubGFiLmxhc3NvIDwtIGlmZWxzZShwcmVkaWN0X2xhc3NvID5vcHQuY3V0Lmxhc3NvLCAxLCAwKQ0KI3ByZWQubGFiLnJpZGdlPC0gaWZlbHNlKHByZWRpY3RfcmlkZ2UgPm9wdC5jdXQucmlkZ2UsIDEsIDApDQojcHJlZC5sYWIuZWxhc3RpYzwtIGlmZWxzZShwcmVkaWN0X2VsYXN0aWMgPm9wdC5jdXQuZWxhc3RpYywgMSwgMCkNCg0KDQpuZXdfdGhyZXNob2xkIDwtIDAuNSAgIyBUcnkgYWRqdXN0aW5nIHRoaXMNCnByZWQubGFiLmxhc3NvIDwtIGlmZWxzZShwcmVkaWN0X2xhc3NvID4gbmV3X3RocmVzaG9sZCwgMSwgMCkNCnByZWQubGFiLnJpZGdlPC0gaWZlbHNlKHByZWRpY3RfcmlkZ2UgPm5ld190aHJlc2hvbGQsIDEsIDApDQpwcmVkLmxhYi5lbGFzdGljPC0gaWZlbHNlKHByZWRpY3RfZWxhc3RpYyA+bmV3X3RocmVzaG9sZCwgMSwgMCkNCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgQ29udmVydCBwcmVkaWN0aW9ucyB0byBmYWN0b3JzDQpwcmVkLmxhYi5sYXNzby5mY3QgPC0gYXMuZmFjdG9yKHByZWQubGFiLmxhc3NvKQ0KcHJlZC5sYWIucmlkZ2UuZmN0IDwtIGFzLmZhY3RvcihwcmVkLmxhYi5yaWRnZSkNCnByZWQubGFiLmVsYXN0aWMuZmN0IDwtIGFzLmZhY3RvcihwcmVkLmxhYi5lbGFzdGljKQ0KeV90ZXN0IDwtIGZhY3RvcihpZmVsc2UoeV90ZXN0ID09IDIsIDEsIDApLCBsZXZlbHMgPSBjKDAsIDEpKQ0KDQojdGFibGUocHJlZC5sYWIubGFzc28uZmN0KQ0KI3RhYmxlKHlfdGVzdCkNCg0KIyBDb25mdXNpb24gTWF0cml4IGFuZCBNZXRyaWNzDQpjb25mdXNpb24ubGFzc28gPC0gY29uZnVzaW9uTWF0cml4KHByZWQubGFiLmxhc3NvLmZjdCwgeV90ZXN0KQ0KY29uZnVzaW9uLnJpZGdlPC0gY29uZnVzaW9uTWF0cml4KHByZWQubGFiLnJpZGdlLmZjdCwgeV90ZXN0KQ0KY29uZnVzaW9uLmVsYXN0aWMgPC0gY29uZnVzaW9uTWF0cml4KHByZWQubGFiLmVsYXN0aWMuZmN0LCB5X3Rlc3QpDQoNCiMjIENvbW1vbmx5IHVzZWQgcGVyZm9ybWFuY2UgbWVhc3VyZWQNClBlcmZNZWFzdXJlcyA8LSBjYmluZChsYXNzbyA9IGNvbmZ1c2lvbi5sYXNzbyRieUNsYXNzLCANCiAgICAgICAgICAgICAgICAgICAgIHJpZGdlID0gY29uZnVzaW9uLnJpZGdlJGJ5Q2xhc3MsIA0KICAgICAgICAgICAgICAgICAgICAgZWxhc3RpYyA9IGNvbmZ1c2lvbi5lbGFzdGljJGJ5Q2xhc3MpDQpwYW5kZXIoUGVyZk1lYXN1cmVzKQ0KDQoNCmBgYA0KDQoNCiMjIFJPQyBBbmFseXNpcw0KDQoNClRoZSBST0MgY3VydmUgY29tcGFyZXMgdGhlIHBlcmZvcm1hbmNlIG9mIHRocmVlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxz4oCUTEFTU08sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXTigJRiYXNlZCBvbiB0aGVpciBhYmlsaXR5IHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gY2xhc3Nlcy4gVGhlIGFyZWEgdW5kZXIgdGhlIGN1cnZlIChBVUMpIHZhbHVlcyBpbmRpY2F0ZSB0aGF0IGFsbCB0aHJlZSBtb2RlbHMgcGVyZm9ybSBzaW1pbGFybHksIHdpdGggRWxhc3RpYyBOZXQgYWNoaWV2aW5nIHRoZSBoaWdoZXN0IEFVQyAoMC44NDEpLCBmb2xsb3dlZCBjbG9zZWx5IGJ5IExBU1NPICgwLjg0KSBhbmQgUmlkZ2UgKDAuODM4KS4gVGhlc2UgdmFsdWVzIHN1Z2dlc3QgdGhhdCB0aGUgbW9kZWxzIGhhdmUgZ29vZCBwcmVkaWN0aXZlIGNhcGFiaWxpdGllcywgd2l0aCBFbGFzdGljIE5ldCBzbGlnaHRseSBvdXRwZXJmb3JtaW5nIHRoZSBvdGhlcnMgaW4gdGVybXMgb2YgY2xhc3NpZmljYXRpb24gYWNjdXJhY3kuDQoNCmBgYHtyfQ0KDQojIGxpYnJhcnkocFJPQykNCiMgUHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggbW9kZWw6IHR5cGUgPSAicmVzcG9uc2UiDQpwcm9iX2xhc3NvIDwtIHByZWRpY3QobGFzc29fbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByb2JfcmlkZ2UgPC0gcHJlZGljdChyaWRnZV9tb2RlbF9vcHQsIG5ld3ggPSBYX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJvYl9lbGFzdGljIDwtIHByZWRpY3QoZWxhc3RpY19tb2RlbF9vcHQsIG5ld3ggPSBYX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIENvbXB1dGUgUk9DIGN1cnZlczogcm9jIG9iamVjdCBjb250YWlucyBhIGxvdCBpbmZvcm1hdGlvbiBpbmNsdWRpbmcNCiMgc2Vuc2l0aXZpdHksIHNwZWNpZmljaXR5LCBBVUMsIGV0Yy4NCnJvY19sYXNzbyA8LSByb2MoeV90ZXN0LCBwcm9iX2xhc3NvKQ0Kcm9jX3JpZGdlIDwtIHJvYyh5X3Rlc3QsIHByb2JfcmlkZ2UpDQpyb2NfZWxhc3RpYyA8LSByb2MoeV90ZXN0LCBwcm9iX2VsYXN0aWMpDQoNCiMgQ29tcHV0ZSBBVUMgdmFsdWVzDQphdWNfbGFzc28gPC0gYXVjKHJvY19sYXNzbykNCmF1Y19yaWRnZSA8LSBhdWMocm9jX3JpZGdlKQ0KYXVjX2VsYXN0aWMgPC0gYXVjKHJvY19lbGFzdGljKQ0KDQojIyBMQVNTTw0Kc2VuLmxhc3NvIDwtIHJvY19sYXNzbyRzZW5zaXRpdml0aWVzDQpzcGUubGFzc28gPC0gcm9jX2xhc3NvJHNwZWNpZmljaXRpZXMNCmF1Yy5sYXNzbyA8LSByb2NfbGFzc28kYXVjDQoNCiMjIFJpZGdlDQpzZW4ucmlkZ2UgPC0gcm9jX3JpZGdlJHNlbnNpdGl2aXRpZXMNCnNwZS5yaWRnZSA8LSByb2NfcmlkZ2Ukc3BlY2lmaWNpdGllcw0KYXVjLnJpZGdlIDwtIHJvY19yaWRnZSRhdWMNCg0KIyMgRWxhc3RpYyBOZXQNCnNlbi5lbGFzdGljIDwtIHJvY19lbGFzdGljJHNlbnNpdGl2aXRpZXMNCnNwZS5lbGFzdGljIDwtIHJvY19lbGFzdGljJHNwZWNpZmljaXRpZXMNCmF1Yy5lbGFzdGljIDwtIHJvY19lbGFzdGljJGF1Yw0KDQojIyBQbG90dGluZyB0aGUgUk9DIGN1cnZlczogdGhyZWUgY29sb3JzIC0gZ3JlZW4sIG9yYW5nZSwgYW5kIHB1cnBsZQ0KDQpwbG90KDEtc3BlLmxhc3NvLCBzZW4ubGFzc28sIA0KICAgICB0eXBlID0gImwiLA0KICAgICBjb2wgPSAiZ3JlZW4iLCANCiAgICAgeGxpbT1jKDAsMSksDQogICAgIHhsYWIgPSAiMSAtIHNwZWNpZmljaXR5IiwNCiAgICAgeWxhYiA9ICJzZW5zaXRpdml0eSIsDQogICAgIG1haW4gPSAiUk9DIEN1cnZlcyBmb3IgTEFTU08sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXQiKQ0KbGluZXMoMS1zcGUucmlkZ2UsIHNlbi5yaWRnZSwgY29sID0gIm9yYW5nZSIpDQpsaW5lcygxLXNwZS5lbGFzdGljLCBzZW4uZWxhc3RpYywgY29sID0gInB1cnBsZSIpDQphYmxpbmUoMCwxLCB0eXBlID0gImwiLCBsdHkgPSAyLCBjb2wgPSAic3RlZWxibHVlIiwgbHdkID0gMSkNCg0KIyBBZGQgbGVnZW5kDQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gYyhwYXN0ZSgiTEFTU08gKEFVQyA9Iiwgcm91bmQoYXVjX2xhc3NvLCAzKSwgIikiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIlJpZGdlIChBVUMgPSIsIHJvdW5kKGF1Y19yaWRnZSwgMyksICIpIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJFbGFzdGljIE5ldCAoQVVDID0iLCByb3VuZChhdWNfZWxhc3RpYywgMyksICIpIikpLA0KICAgICAgIGNvbCA9IGMoImdyZWVuIiwgIm9yYW5nZSIsICJwdXJwbGUiKSwgbHR5ID0gMSwgY2V4ID0gMC44LCBidHkgPSAibiIpDQoNCmBgYA0KDQojIyBTdXBwb3J0IFZlY3RvciBNYWNoaW5lIGZvciBDbGFzc2lmaWNhdGlvbg0KDQpUaGUgcmVzdWx0cyByZXByZXNlbnQgYSBjb25mdXNpb24gbWF0cml4IGZyb20gYSBTdXBwb3J0IFZlY3RvciBNYWNoaW5lIChTVk0pIGNsYXNzaWZpY2F0aW9uIG1vZGVsLCB3aGVyZTogIA0KDQotICoqMjA2KiogdHJ1ZSBuZWdhdGl2ZXMgKGNvcnJlY3RseSBjbGFzc2lmaWVkIGFzIGNsYXNzIDApICANCi0gKioxNCoqIGZhbHNlIHBvc2l0aXZlcyAobWlzY2xhc3NpZmllZCBhcyBjbGFzcyAxIGJ1dCBhY3R1YWxseSBjbGFzcyAwKSAgDQotICoqNTgqKiBmYWxzZSBuZWdhdGl2ZXMgKG1pc2NsYXNzaWZpZWQgYXMgY2xhc3MgMCBidXQgYWN0dWFsbHkgY2xhc3MgMSkgIA0KLSAqKjYxKiogdHJ1ZSBwb3NpdGl2ZXMgKGNvcnJlY3RseSBjbGFzc2lmaWVkIGFzIGNsYXNzIDEpICANCg0KIyMjICoqQW5hbHlzaXM6KiogIA0KMS4gKipBY2N1cmFjeSoqOiAgVGhlIG1vZGVsIGFjaGlldmVzIGFuIGFjY3VyYWN5IG9mIGFwcHJveGltYXRlbHkgKio3OC43NiUqKi4NCg0KMi4gKipQcmVjaXNpb24gKFBvc2l0aXZlIFByZWRpY3RpdmUgVmFsdWUpKiogZm9yIENsYXNzIDE6IFdoZW4gdGhlIG1vZGVsIHByZWRpY3RzIGNsYXNzIDEsIGl0IGlzIGNvcnJlY3QgYWJvdXQgKio4MS4zMyUqKiBvZiB0aGUgdGltZS4NCg0KMy4gKipSZWNhbGwgKFNlbnNpdGl2aXR5IG9yIFRydWUgUG9zaXRpdmUgUmF0ZSkqKiBmb3IgQ2xhc3MgMTogVGhlIG1vZGVsIGNvcnJlY3RseSBpZGVudGlmaWVzICoqNTEuMjYlKiogb2YgYWN0dWFsIGNsYXNzIDEgaW5zdGFuY2VzLCBpbmRpY2F0aW5nIHRoYXQgaXQgbWlzc2VzIGEgc2lnbmlmaWNhbnQgbnVtYmVyIG9mIHBvc2l0aXZlcy4NCg0KNC4gKipTcGVjaWZpY2l0eSAoVHJ1ZSBOZWdhdGl2ZSBSYXRlKSoqOiBUaGUgbW9kZWwgY29ycmVjdGx5IGlkZW50aWZpZXMgKio5My42MyUqKiBvZiBhY3R1YWwgY2xhc3MgMCBpbnN0YW5jZXMsIHNob3dpbmcgc3Ryb25nIHBlcmZvcm1hbmNlIGluIHJlY29nbml6aW5nIG5lZ2F0aXZlcy4NCg0KNS4gKipPcHRpbWFsIEN1dG9mZjoqKiAqKjAuMTc0MioqICANCiAgIC0gVGhlIGNsYXNzaWZpY2F0aW9uIHRocmVzaG9sZCAoY3V0b2ZmKSB3YXMgb3B0aW1pemVkIHRvICoqMC4xNzQyKiosIG1lYW5pbmcgdGhhdCBhbnkgcHJlZGljdGVkIHByb2JhYmlsaXR5IGFib3ZlIHRoaXMgaXMgY2xhc3NpZmllZCBhcyBjbGFzcyAxLiAgDQogICAtIEEgbG93IHRocmVzaG9sZCBsaWtlIHRoaXMgc3VnZ2VzdHMgdGhlIG1vZGVsIGlzIGRlc2lnbmVkIHRvIGJlIG1vcmUgc2Vuc2l0aXZlIHRvIGRldGVjdGluZyBjbGFzcyAxLCBsaWtlbHkgdG8gcmVkdWNlIGZhbHNlIG5lZ2F0aXZlcy4gSG93ZXZlciwgdGhpcyBhbHNvIGluY3JlYXNlcyB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZS4NCg0KIA0KVGhlIFNWTSBjbGFzc2lmaWVyIHBlcmZvcm1zIHdlbGwgaW4gaWRlbnRpZnlpbmcgY2xhc3MgMCAoaGlnaCBzcGVjaWZpY2l0eSkgYnV0IHN0cnVnZ2xlcyB3aXRoIGNsYXNzIDEgKGxvdyByZWNhbGwpLiBUaGUgb3B0aW1hbCB0aHJlc2hvbGQgKDAuMTc0Mikgd2FzIGNob3NlbiB0byBiYWxhbmNlIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSwgYnV0IHRoZSByZWNhbGwgZm9yIGNsYXNzIDEgcmVtYWlucyByZWxhdGl2ZWx5IGxvdy4gSWYgbWlzY2xhc3NpZnlpbmcgY2xhc3MgMSBpcyBjb3N0bHksIGFkanVzdGluZyB0aGUgdGhyZXNob2xkIG9yIG1vZGVsIHR1bmluZyAoZS5nLiwgY2hhbmdpbmcga2VybmVsIGZ1bmN0aW9ucywgcmVndWxhcml6YXRpb24gcGFyYW1ldGVycykgbWF5IGltcHJvdmUgcmVjYWxsLg0KDQoNCmBgYHtyfQ0KDQoNCiMgTG9hZCB0aGUgZGF0YXNldA0KDQpkZiRCYW5rcnVwdCA8LSBhcy5mYWN0b3IoZGYkQmFua3J1cHQpDQoNCiMgVHJhaW4tdGVzdCBzcGxpdA0Kc2V0LnNlZWQoMTIzKQ0KaW5kZXggPC0gc2FtcGxlKDE6bnJvdyhkZiksIDAuNyAqIG5yb3coZGYpKQ0KdHJhaW4uZGF0YSA8LSBkZltpbmRleCwgXQ0KdGVzdC5kYXRhIDwtIGRmWy1pbmRleCwgXQ0KDQojIFNldCB1cCBjdXN0b20gY3Jvc3MtdmFsaWRhdGlvbiBjb250cm9sDQp0dW5lX2NvbnRyb2wgPC0gdHVuZS5jb250cm9sKGNyb3NzID0gNSwgbnJlcGVhdCA9IDEpDQoNCiMgUGVyZm9ybSBhIGdyaWQgc2VhcmNoIGZvciB0aGUgYmVzdCBoeXBlcnBhcmFtZXRlcnMNCnR1bmUuUkJGIDwtIHR1bmUoDQogIHN2bSwNCiAgQmFua3J1cHQgfiAuLA0KICBkYXRhID0gdHJhaW4uZGF0YSwNCiAga2VybmVsID0gInJhZGlhbCIsDQogIHJhbmdlcyA9IGxpc3QoY29zdCA9IDEwXigtMToyKSwgZ2FtbWEgPSBjKDAuMSwgMC41LCAxLCAyKSksDQogIHR1bmVjb250cm9sID0gdHVuZV9jb250cm9sDQopDQoNCiMgRXh0cmFjdCB0aGUgYmVzdCBtb2RlbA0KYmVzdC5SQkYgPC0gdHVuZS5SQkYkYmVzdC5tb2RlbA0KDQojIFRyYWluIGZpbmFsIG1vZGVsIHdpdGggcHJvYmFiaWxpdHkgZXN0aW1hdGlvbg0KdHVuZWQuc3ZtIDwtIHN2bSgNCiAgQmFua3J1cHQgfiAuLA0KICBkYXRhID0gdHJhaW4uZGF0YSwNCiAga2VybmVsID0gInJhZGlhbCIsDQogIGNvc3QgPSBiZXN0LlJCRiRjb3N0LA0KICBnYW1tYSA9IGJlc3QuUkJGJGdhbW1hLA0KICBwcm9iYWJpbGl0eSA9IFRSVUUNCikNCg0KIyBQcmVkaWN0IHByb2JhYmlsaXRpZXMNCnByZWQucHJvYnMgPC0gcHJlZGljdCh0dW5lZC5zdm0sIHRlc3QuZGF0YSwgcHJvYmFiaWxpdHkgPSBUUlVFKQ0KcHJlZC5wcm9icyA8LSBhdHRyKHByZWQucHJvYnMsICJwcm9iYWJpbGl0aWVzIilbLCAyXSAgIyBFeHRyYWN0IHByb2JhYmlsaXRpZXMgZm9yIGNsYXNzICIxIg0KDQojIENvbXB1dGUgb3B0aW1hbCBjdXRvZmYgYnkgbWluaW1pemluZyBkaXN0YW5jZSB0byAoMCwxKSBpbiBST0Mgc3BhY2UNCnByZWQgPC0gcHJlZGljdGlvbihwcmVkLnByb2JzLCB0ZXN0LmRhdGEkQmFua3J1cHQpDQpwZXJmIDwtIHBlcmZvcm1hbmNlKHByZWQsICJ0cHIiLCAiZnByIikNCg0KY3V0b2ZmcyA8LSBkYXRhLmZyYW1lKA0KICBjdXRvZmYgPSBwZXJmQGFscGhhLnZhbHVlc1tbMV1dLA0KICB0cHIgPSBwZXJmQHkudmFsdWVzW1sxXV0sDQogIGZwciA9IHBlcmZAeC52YWx1ZXNbWzFdXQ0KKQ0KY3V0b2ZmcyRkaXN0YW5jZSA8LSBzcXJ0KCgxIC0gY3V0b2ZmcyR0cHIpXjIgKyBjdXRvZmZzJGZwcl4yKQ0Kb3B0aW1hbC5jdXRvZmYgPC0gY3V0b2ZmcyRjdXRvZmZbd2hpY2gubWluKGN1dG9mZnMkZGlzdGFuY2UpXQ0KDQojIEFwcGx5IG9wdGltYWwgY3V0b2ZmDQpwcmVkLm9wdGltYWwuY2xhc3MgPC0gaWZlbHNlKHByZWQucHJvYnMgPiBvcHRpbWFsLmN1dG9mZiwgMSwgMCkNCg0KIyBDb25mdXNpb24gbWF0cml4IHdpdGggb3B0aW1hbCBjdXRvZmYNCmNvbmZ1c2lvbi5tYXRyaXgub3B0aW1hbCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkLm9wdGltYWwuY2xhc3MsIEFjdHVhbCA9IHRlc3QuZGF0YSRCYW5rcnVwdCkNCnByaW50KGNvbmZ1c2lvbi5tYXRyaXgub3B0aW1hbCkNCg0KDQoNCiMgUHJpbnQgb3B0aW1hbCBjdXRvZmYgdmFsdWUNCnByaW50KHBhc3RlKCJPcHRpbWFsIEN1dG9mZjogIiwgb3B0aW1hbC5jdXRvZmYpKQ0KDQoNCg0KYGBgDQoNCg0KDQojIyBBY2N1cmFjeQ0KDQoNCmBgYHtyfQ0KDQojIENhbGN1bGF0ZSBhY2N1cmFjeQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZnVzaW9uLm1hdHJpeC5vcHRpbWFsKSkgLyBzdW0oY29uZnVzaW9uLm1hdHJpeC5vcHRpbWFsKQ0KY2F0KCJcblxuIEFjY3VyYWN5OiIsIGFjY3VyYWN5LCAiXG4iKQ0KDQpgYGANCg0KDQoNCg0KDQoNCg0KIyMgUk9DDQoNClRoZSBST0MgY3VydmUgY29tcGFyZXMgdGhlIGNsYXNzaWZpY2F0aW9uIHBlcmZvcm1hbmNlIG9mIHRocmVlIG1vZGVsczogU3VwcG9ydCBWZWN0b3IgTWFjaGluZSAoU1ZNKSB3aXRoIGEgbGluZWFyIGtlcm5lbCwgU1ZNIHdpdGggYSByYWRpYWwga2VybmVsLCBhbmQgbG9naXN0aWMgcmVncmVzc2lvbi4gVGhlIEFyZWEgVW5kZXIgdGhlIEN1cnZlIChBVUMpIHZhbHVlcyBpbmRpY2F0ZSB0aGF0IHRoZSByYWRpYWwga2VybmVsIFNWTSBhY2hpZXZlcyB0aGUgYmVzdCBwZXJmb3JtYW5jZSAoQVVDID0gMC44NzU2KSwgZm9sbG93ZWQgYnkgdGhlIGxpbmVhciBrZXJuZWwgU1ZNIChBVUMgPSAwLjg0OTMpLCBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiAoQVVDID0gMC44MzE5KS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSByYWRpYWwga2VybmVsIFNWTSBwcm92aWRlcyB0aGUgaGlnaGVzdCBkaXNjcmltaW5hdG9yeSBwb3dlciBpbiBkaXN0aW5ndWlzaGluZyBiZXR3ZWVuIGNsYXNzZXMsIG1ha2luZyBpdCB0aGUgbW9zdCBlZmZlY3RpdmUgbW9kZWwgZm9yIHRoaXMgY2xhc3NpZmljYXRpb24gdGFzay4NCg0KYGBge3J9DQoNCg0KIyMNCiMjIFNldCB1cCBjdXN0b20gY3Jvc3MtdmFsaWRhdGlvbiBjb250cm9sDQp0dW5lLmNvbnRyb2wgPC0gdHVuZS5jb250cm9sKA0KICBjcm9zcyA9IDUsICAjIFVzZSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgdGhlIGRlZmF1bHQgaXMgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uDQogIG5yZXBlYXQgPSAxICMgTnVtYmVyIG9mIHJlcGV0aXRpb25zIChmb3IgcmVwZWF0ZWQgY3Jvc3MtdmFsaWRhdGlvbikNCikNCiMjIA0KIyMgUGVyZm9ybSBhIGdyaWQgc2VhcmNoIGZvciB0aGUgYmVzdCBoeXBlcnBhcmFtZXRlcnMNCnR1bmUubGluIDwtIHR1bmUoDQogIHN2bSwgICAgICAgICAgIyB1c2luZyB0aGUgcHJpbWFyeSBzdm0oKSBhbGdvcml0aG0gdG8gdHVuZSBwYXJhbWV0ZXINCiAgQmFua3J1cHQgfiAuLCAjIG1vZGVsIGZvcm11bGENCiAgZGF0YSA9IHRyYWluLmRhdGEsDQogIGtlcm5lbCA9ICJsaW5lYXIiLCAgICAjIFlvdSBjYW4gY2hhbmdlIHRoZSBrZXJuZWwgaWYgbmVlZGVkDQogIHJhbmdlcyA9IGxpc3QoDQogICAgY29zdCA9IDEwXigtMToyKSAgICMgdHVuZSB0aGUgaHlwZXJwYXJhbWV0ZXIgQyBpbiB0aGUgbG9zcyBmdW5jdGlvbg0KICApLA0KICB0dW5lY29udHJvbCA9IHR1bmUuY29udHJvbCAgIyBVc2UgY3VzdG9tIGNyb3NzLXZhbGlkYXRpb24gc2V0dGluZ3MNCikNCiMgUHJpbnQgdGhlIHR1bmluZyByZXN1bHRzIGZvciBpbnNwZWN0aW9uDQojIHByaW50KHR1bmVfcmVzdWx0KQ0KIyMNCiMjIEV4dHJhY3QgdGhlIGJlc3QgbW9kZWwgYW5kIGh5cGVycGFyYW1ldGVycw0KYmVzdC5saW4gPC0gdHVuZS5saW4kYmVzdC5tb2RlbA0KYmVzdC5jb3N0LmxpbiA8LSBiZXN0LmxpbiRjb3N0DQoNCnR1bmUuUkJGIDwtIHR1bmUoDQogIHN2bSwgDQogIEJhbmtydXB0IH4gLiwgDQogIGRhdGEgPSB0cmFpbi5kYXRhLA0KICBrZXJuZWwgPSAicmFkaWFsIiwNCiAgcmFuZ2VzID0gbGlzdCgNCiAgICBjb3N0ID0gMTBeKC0xOjIpLCAgIyBUdW5lIGNvc3QNCiAgICBnYW1tYSA9IDEwXigtMzoxKSAgIyBUdW5lIGdhbW1hDQogICksDQogIHR1bmVjb250cm9sID0gdHVuZS5jb250cm9sDQopDQoNCiMgRXh0cmFjdCBiZXN0IGh5cGVycGFyYW1ldGVycw0KYmVzdC5jb3N0LlJCRiA8LSB0dW5lLlJCRiRiZXN0Lm1vZGVsJGNvc3QNCmJlc3QuZ2FtbWEuUkJGIDwtIHR1bmUuUkJGJGJlc3QubW9kZWwkZ2FtbWENCg0KIyBQcmludCB0aGUgYmVzdCBoeXBlcnBhcmFtZXRlcnMgZm9yIGluc3BlY3Rpb24NCiMgY2F0KCJCZXN0IENvc3Q6IiwgYmVzdF9jb3N0LCAiXG4iKQ0KIyBjYXQoIkJlc3QgR2FtbWE6IiwgYmVzdF9nYW1tYSwgIlxuIikNCiMjDQojIyBUcmFpbiB0aGUgZmluYWwgU1ZNIG1vZGVsIHdpdGggdGhlIGJlc3QgaHlwZXJwYXJhbWV0ZXJzDQpmaW5hbC5saW4gPC0gc3ZtKA0KICBCYW5rcnVwdCB+IC4sDQogIGRhdGEgPSB0cmFpbi5kYXRhLA0KICBrZXJuZWwgPSAibGluZWFyIiwNCiAgY29zdCA9IGJlc3QuY29zdC5saW4sDQogIHByb2JhYmlsaXR5ID0gVFJVRQ0KKQ0KDQojIyBSZXF1ZXN0IHRvIHJldHVybiBwcm9iYWJpbGl0aWVzIGluIGZpbmFsLlJCRg0KDQpmaW5hbC5SQkYgPC0gc3ZtKA0KICBCYW5rcnVwdCB+IC4sDQogIGRhdGEgPSB0cmFpbi5kYXRhLA0KICBrZXJuZWwgPSAicmFkaWFsIiwNCiAgY29zdCA9IGJlc3QuY29zdC5SQkYsDQogIGdhbW1hID0gYmVzdC5nYW1tYS5SQkYsDQogIHByb2JhYmlsaXR5ID0gVFJVRQ0KKQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyMgIGxvZ2lzdGljIHJlZ3Jlc3Npb24NCmxvZ2l0LmZpdCA8LSBnbG0oQmFua3J1cHQgfiAuLCBkYXRhID0gdHJhaW4uZGF0YSwgZmFtaWx5ID0gYmlub21pYWwpDQpBSUMubG9naXQgPC0gc3RlcChsb2dpdC5maXQsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSAwKQ0KcHJlZC5sb2dpdCA8LSBwcmVkaWN0KEFJQy5sb2dpdCwgdGVzdC5kYXRhLCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBST0MgQ3VydmUgYW5kIEFVQw0KcHJlZC5wcm9iLmxpbiA8LSBwcmVkaWN0KGZpbmFsLmxpbiwgdGVzdC5kYXRhLCBwcm9iYWJpbGl0eSA9IFRSVUUpDQpwcmVkLnByb2IuUkJGIDwtIHByZWRpY3QoZmluYWwuUkJGLCB0ZXN0LmRhdGEsIHByb2JhYmlsaXR5ID0gVFJVRSkNCiMjDQojIyBleHRyYWN0aW5nIHByb2JhYmlsaXRpZXMNCnByb2IubGluZWFyIDwtIGF0dHIocHJlZC5wcm9iLmxpbiwgInByb2JhYmlsaXRpZXMiKVssIDJdDQpwcm9iLnJhZGlhbCA8LSBhdHRyKHByZWQucHJvYi5SQkYsICJwcm9iYWJpbGl0aWVzIilbLCAyXQ0KIyMjDQpyb2NfbGluIDwtIHJvYyh0ZXN0LmRhdGEkQmFua3J1cHQsIHByb2IubGluZWFyKQ0Kcm9jX1JCRiA8LSByb2ModGVzdC5kYXRhJEJhbmtydXB0LCBwcm9iLnJhZGlhbCkNCnJvY19sb2dpdCA8LSByb2ModGVzdC5kYXRhJEJhbmtydXB0LCBwcmVkLmxvZ2l0KQ0KIyMjIFNlbi1TcGUNCmxpbi5zZW4gPC0gcm9jX2xpbiRzZW5zaXRpdml0aWVzDQpsaW4uc3BlIDwtIHJvY19saW4kc3BlY2lmaWNpdGllcw0KcmFkLnNlbiA8LSByb2NfUkJGJHNlbnNpdGl2aXRpZXMNCnJhZC5zcGUgPC0gcm9jX1JCRiRzcGVjaWZpY2l0aWVzDQpsb2dpdC5zZW4gPC0gcm9jX2xvZ2l0JHNlbnNpdGl2aXRpZXMNCmxvZ2l0LnNwZSA8LSByb2NfbG9naXQkc3BlY2lmaWNpdGllcw0KIyMgQVVDDQphdWMubGluIDwtIHJvY19saW4kYXVjDQphdWMucmFkIDwtIHJvY19SQkYkYXVjDQphdWMubG9naXQgPC0gcm9jX2xvZ2l0JGF1Yw0KIyMgUGxvdHRpbmcgUk9DIGN1cnZlcw0KDQpwbG90KDEtbGluLnNwZSwgbGluLnNlbiwgIA0KICAgICB4bGFiID0gIjEgLSBzcGVjaWZpY2l0eSIsDQogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLA0KICAgICBjb2wgPSAiZGFya3JlZCIsDQogICAgIHR5cGUgPSAibCIsDQogICAgIGx0eSA9IDEsDQogICAgIGx3ZCA9IDEsDQogICAgIG1haW4gPSAiUk9DIEN1cnZlcyBvZiBTVk0iKQ0KbGluZXMoMS1yYWQuc3BlLCByYWQuc2VuLCANCiAgICAgIGNvbCA9ICJibHVlIiwNCiAgICAgIGx0eSA9IDEsDQogICAgICBsd2QgPSAxKQ0KbGluZXMoMS1sb2dpdC5zcGUsIGxvZ2l0LnNlbiwgICAgICANCiAgICAgIGNvbCA9ICJvcmFuZ2UiLA0KICAgICAgbHR5ID0gMSwNCiAgICAgIGx3ZCA9IDEpDQphYmxpbmUoMCwxLCBjb2wgPSAic2t5Ymx1ZTMiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYWJsaW5lKHY9YygwLjA0OSwwLjE1MSksIGx0eSA9IDMsIGNvbCA9ICJkYXJrZ3JlZW4iKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGMoIkxpbmVhciBLZXJuZWwiLCAiUmFkaWFsIEtlcm5lbCIsICJMb2dpc3RpYyBSZWdyZXNzaW9uIiksDQogICAgICAgbHR5ID0gYygxLDEsMSksIGx3ZCA9IHJlcCgxLDMpLA0KICAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIiwgIm9yYW5nZSIpLA0KICAgICAgIGJ0eT0ibiIsY2V4ID0gMC44KQ0KIyMgYW5ub3RhdGlvbiAtIEFVQw0KdGV4dCgwLjgsIDAuNDYsIHBhc3RlKCJMaW5lYXIgQVVDOiAiLCByb3VuZChhdWMubGluLDQpKSwgY2V4ID0gMC44KQ0KdGV4dCgwLjgsIDAuNCwgcGFzdGUoIlJhZGlhbCBBVUM6ICIsIHJvdW5kKGF1Yy5yYWQsNCkpLCBjZXggPSAwLjgpDQp0ZXh0KDAuOCwgMC4zNCwgcGFzdGUoIkxvZ2lzdGljIEFVQzogIiwgcm91bmQoYXVjLmxvZ2l0LDQpKSwgY2V4ID0gMC44KQ0KDQpgYGANCg0KDQojIyBNb2RlbCBDb21wYXJpc29uDQoNClJlZ3VsYXJpemVkIENsYXNzaWZpY2F0aW9uIHZzIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUgQ2xhc3NpZmljYXRpb246DQoNClJlZ3VsYXJpemVkIGNsYXNzaWZpY2F0aW9uIChMYXNzbywgUmlkZ2UsIGFuZCBFbGFzdGljIE5ldCkgYW5kIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUgKFNWTSkgY2xhc3NpZmljYXRpb24gZXhoaWJpdCBkaXN0aW5jdCBzdHJlbmd0aHMuIFJlZ3VsYXJpemVkIGNsYXNzaWZpY2F0aW9uIG1vZGVscyBkZW1vbnN0cmF0ZSBoaWdoIHNlbnNpdGl2aXR5ICg5Ny4wNCUpIGFjcm9zcyBhbGwgbWV0aG9kcywgbWVhbmluZyB0aGV5IGVmZmVjdGl2ZWx5IGNhcHR1cmUgcG9zaXRpdmUgY2FzZXMuIEhvd2V2ZXIsIHRoZWlyIHNwZWNpZmljaXR5IGlzIHJlbGF0aXZlbHkgbG93IChyYW5naW5nIGZyb20gMzUuNzElIHRvIDM5LjI5JSksIGluZGljYXRpbmcgdGhleSBzdHJ1Z2dsZSB3aXRoIGNvcnJlY3RseSBpZGVudGlmeWluZyBuZWdhdGl2ZXMuIEluIGNvbnRyYXN0LCB0aGUgU1ZNIGNsYXNzaWZpZXIgYWNoaWV2ZXMgbXVjaCBoaWdoZXIgc3BlY2lmaWNpdHkgKDkzLjYzJSksIG1lYW5pbmcgaXQgaXMgZXhjZWxsZW50IGF0IGlkZW50aWZ5aW5nIG5lZ2F0aXZlIGNhc2VzLCBidXQgaXRzIHNlbnNpdGl2aXR5IGlzIHNpZ25pZmljYW50bHkgbG93ZXIgKDUxLjI2JSksIGxlYWRpbmcgdG8gYSBoaWdoZXIgcmF0ZSBvZiBtaXNzZWQgcG9zaXRpdmUgY2FzZXMuIEFkZGl0aW9uYWxseSwgdGhlIFNWTSBtb2RlbCBoYXMgYW4gb3ZlcmFsbCBhY2N1cmFjeSBvZiA3OC43NiUsIGJhbGFuY2luZyBwcmVjaXNpb24gYW5kIHJlY2FsbCB3aXRoIGFuIG9wdGltaXplZCBjdXRvZmYgb2YgMC4xNzQyLiBVbHRpbWF0ZWx5LCByZWd1bGFyaXplZCBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgYXJlIGJldHRlciBzdWl0ZWQgd2hlbiBjYXB0dXJpbmcgcG9zaXRpdmVzIGlzIGNyaXRpY2FsLCB3aGVyZWFzIFNWTSBpcyBwcmVmZXJhYmxlIHdoZW4gY29ycmVjdGx5IGlkZW50aWZ5aW5nIG5lZ2F0aXZlcyBpcyBhIHByaW9yaXR5Lg0KDQoNCg==